diff --git a/ASTPlayground/ASTPlayground.delphilsp.json b/ASTPlayground/ASTPlayground.delphilsp.json
index 78d1f1f..5348c6a 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.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.Visitor", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Visitor.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.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.Data.Keyword", "file": "file:///T%3A/Myc/Src/Data/Myc.Data.Keyword.pas" }, { "name": "Myc.Ast.Compiler.TCO", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.TCO.pas" }, { "name": "Myc.Ast.Compiler.Binder", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Binder.pas" }, { "name": "Myc.Ast.Compiler.Lowering", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Lowering.pas" }, { "name": "Myc.Ast.Compiler.TypeChecker", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.TypeChecker.pas" }, { "name": "Myc.Ast.Compiler.Macros", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Macros.pas" }, { "name": "Myc.Ast.Compiler.Specializer", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Specializer.pas" }, { "name": "Myc.Ast.Environment", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Environment.pas" }, { "name": "Myc.Ast.Debugger", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Debugger.pas" }, { "name": "Myc.Ast.Analysis.Purity", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Analysis.Purity.pas" }, { "name": "Myc.Ast.Identities", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Identities.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.pas" }, { "name": "Myc.Fmx.AstEditor.Visualizer", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas" }, { "name": "Myc.Fmx.AstEditor.Workspace", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Workspace.pas" }, { "name": "Myc.Fmx.AstEditor.Layout", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Layout.pas" }, { "name": "Myc.Fmx.AstEditor.Node", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Node.pas" }, { "name": "Myc.Ast.Refactoring.Remove", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Refactoring.Remove.pas" }, { "name": "Myc.Fmx.AstEditor.Render", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Render.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers.Lists", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.Lists.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers.Primitives", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.Primitives.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers.Control", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.Control.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers.Data", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.Data.pas" }, { "name": "Myc.Ast.RTL.TypeRegistry", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.TypeRegistry.pas" }, { "name": "Myc.Trade.Broker", "file": "file:///T%3A/Myc/Src/Myc.Trade.Broker.pas" }, { "name": "Myc.Data.Stream.Pipes", "file": "file:///T%3A/Myc/Src/Data/Myc.Data.Stream.Pipes.pas" }, { "name": "Myc.Data.Stream", "file": "file:///T%3A/Myc/Src/Data/Myc.Data.Stream.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers.Pipes", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.Pipes.pas" }, { "name": "Demo.Finance", "file": "file:///T%3A/Myc/Test/Demo.Finance.pas" } ] , "includeDCUsInUsesCompletion": true, "enableKeyWordCompletion": true, "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.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.Visitor", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Visitor.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.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.Data.Keyword", "file": "file:///T%3A/Myc/Src/Data/Myc.Data.Keyword.pas" }, { "name": "Myc.Ast.Compiler.TCO", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.TCO.pas" }, { "name": "Myc.Ast.Compiler.Binder", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Binder.pas" }, { "name": "Myc.Ast.Compiler.Lowering", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Lowering.pas" }, { "name": "Myc.Ast.Compiler.TypeChecker", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.TypeChecker.pas" }, { "name": "Myc.Ast.Compiler.Macros", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Macros.pas" }, { "name": "Myc.Ast.Compiler.Specializer", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Specializer.pas" }, { "name": "Myc.Ast.Environment", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Environment.pas" }, { "name": "Myc.Ast.Debugger", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Debugger.pas" }, { "name": "Myc.Ast.Analysis.Purity", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Analysis.Purity.pas" }, { "name": "Myc.Ast.Identities", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Identities.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.pas" }, { "name": "Myc.Fmx.AstEditor.Visualizer", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas" }, { "name": "Myc.Fmx.AstEditor.Workspace", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Workspace.pas" }, { "name": "Myc.Fmx.AstEditor.Layout", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Layout.pas" }, { "name": "Myc.Fmx.AstEditor.Node", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Node.pas" }, { "name": "Myc.Ast.Refactoring.Remove", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Refactoring.Remove.pas" }, { "name": "Myc.Fmx.AstEditor.Render", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Render.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers.Lists", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.Lists.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers.Primitives", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.Primitives.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers.Control", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.Control.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers.Data", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.Data.pas" }, { "name": "Myc.Ast.RTL.TypeRegistry", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.TypeRegistry.pas" }, { "name": "Myc.Trade.Broker", "file": "file:///T%3A/Myc/Src/Myc.Trade.Broker.pas" }, { "name": "Myc.Data.Stream.Pipes", "file": "file:///T%3A/Myc/Src/Data/Myc.Data.Stream.Pipes.pas" }, { "name": "Myc.Data.Stream", "file": "file:///T%3A/Myc/Src/Data/Myc.Data.Stream.pas" }, { "name": "Myc.Fmx.AstEditor.Handlers.Pipes", "file": "file:///T%3A/Myc/Src/AST/Myc.Fmx.AstEditor.Handlers.Pipes.pas" }, { "name": "Demo.Finance", "file": "file:///T%3A/Myc/Test/Demo.Finance.pas" }, { "name": "Myc.Ast.Script.Print", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Script.Print.pas" } ] , "includeDCUsInUsesCompletion": true, "enableKeyWordCompletion": true, "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 3db1c38..78aad6c 100644
--- a/ASTPlayground/ASTPlayground.dpr
+++ b/ASTPlayground/ASTPlayground.dpr
@@ -42,7 +42,8 @@ uses
Myc.Data.Stream.Pipes in '..\Src\Data\Myc.Data.Stream.Pipes.pas',
Myc.Data.Stream in '..\Src\Data\Myc.Data.Stream.pas',
Myc.Fmx.AstEditor.Handlers.Pipes in '..\Src\AST\Myc.Fmx.AstEditor.Handlers.Pipes.pas',
- Demo.Finance in '..\Test\Demo.Finance.pas';
+ Demo.Finance in '..\Test\Demo.Finance.pas',
+ Myc.Ast.Script.Print in '..\Src\AST\Myc.Ast.Script.Print.pas';
{$R *.res}
diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj
index e5eb7ab..a55bf4b 100644
--- a/ASTPlayground/ASTPlayground.dproj
+++ b/ASTPlayground/ASTPlayground.dproj
@@ -173,6 +173,7 @@
+
Base
diff --git a/Src/AST/Myc.Ast.Analysis.Purity.pas b/Src/AST/Myc.Ast.Analysis.Purity.pas
index 99eb6b9..505713e 100644
--- a/Src/AST/Myc.Ast.Analysis.Purity.pas
+++ b/Src/AST/Myc.Ast.Analysis.Purity.pas
@@ -7,58 +7,65 @@ uses
Myc.Data.Value,
Myc.Ast.Nodes,
Myc.Ast.Visitor,
- Myc.Ast.Scope;
+ Myc.Ast.Scope,
+ Myc.Ast;
type
- ///
- /// Analyzes an AST to determine if it is referentially transparent and free of side effects.
- ///
+ // Analyzes an AST to determine if it is referentially transparent and free of side effects.
TPurityAnalyzer = class(TAstVisitor)
- protected
- // Default behavior: Visit children. If all children return True, then True.
- // However, we must explicitly define what is allowed.
- function Accept(const Node: IAstNode): Boolean; override;
-
+ strict private
// --- Safe Leaves / Constructs ---
- function VisitConstant(const Node: IConstantNode): Boolean; override;
- function VisitKeyword(const Node: IKeywordNode): Boolean; override;
- function VisitIfExpression(const Node: IIfExpressionNode): Boolean; override;
- function VisitCondExpression(const Node: ICondExpressionNode): Boolean; override; // Replaces Ternary
- function VisitBlockExpression(const Node: IBlockExpressionNode): Boolean; override;
- function VisitRecordLiteral(const Node: IRecordLiteralNode): Boolean; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): Boolean; override;
- function VisitSeriesLength(const Node: ISeriesLengthNode): Boolean; override;
+ function VisitConstant(const Node: IAstNode): Boolean;
+ function VisitKeyword(const Node: IAstNode): Boolean;
+ function VisitIfExpression(const Node: IAstNode): Boolean;
+ function VisitCondExpression(const Node: IAstNode): Boolean;
+ function VisitBlockExpression(const Node: IAstNode): Boolean;
+ function VisitRecordLiteral(const Node: IAstNode): Boolean;
+ function VisitVariableDeclaration(const Node: IAstNode): Boolean;
+ function VisitSeriesLength(const Node: IAstNode): Boolean;
- // --- List Visitors (New) ---
- function VisitParameterList(const Node: IParameterList): Boolean; override;
- function VisitArgumentList(const Node: IArgumentList): Boolean; override;
- function VisitExpressionList(const Node: IExpressionList): Boolean; override;
- function VisitRecordFieldList(const Node: IRecordFieldList): Boolean; override;
- function VisitRecordField(const Node: IRecordFieldNode): Boolean; override;
+ // --- List Visitors (Aggregation Logic: All must be pure) ---
+ function VisitParameterList(const Node: IAstNode): Boolean;
+ function VisitArgumentList(const Node: IAstNode): Boolean;
+ function VisitExpressionList(const Node: IAstNode): Boolean;
+ function VisitRecordFieldList(const Node: IAstNode): Boolean;
+ function VisitRecordField(const Node: IAstNode): Boolean;
// --- Critical Checks ---
- function VisitIdentifier(const Node: IIdentifierNode): Boolean; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): Boolean; override;
- function VisitRecurNode(const Node: IRecurNode): Boolean; override;
+ function VisitIdentifier(const Node: IAstNode): Boolean;
+ function VisitFunctionCall(const Node: IAstNode): Boolean;
+ function VisitRecurNode(const Node: IAstNode): Boolean;
// --- Forbidden Constructs (Side Effects / Unsafe) ---
- function VisitAssignment(const Node: IAssignmentNode): Boolean; override;
- function VisitAddSeriesItem(const Node: IAddSeriesItemNode): Boolean; override;
+ function VisitAssignment(const Node: IAstNode): Boolean;
+ function VisitAddSeriesItem(const Node: IAstNode): Boolean;
- // Allocation is considered pure in this context (it creates a new value, doesn't mutate existing world)
- function VisitCreateSeries(const Node: ICreateSeriesNode): Boolean; override;
+ // Allocation is considered pure in this context
+ function VisitCreateSeries(const Node: IAstNode): Boolean;
- function VisitIndexer(const Node: IIndexerNode): Boolean; override;
- function VisitMemberAccess(const Node: IMemberAccessNode): Boolean; override;
+ function VisitIndexer(const Node: IAstNode): Boolean;
+ function VisitMemberAccess(const Node: IAstNode): Boolean;
- // Ignored / Irrelevant for Runtime Purity (Compile-time constructs or Definitions)
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): Boolean; override;
- function VisitMacroDefinition(const Node: IMacroDefinitionNode): Boolean; override;
- function VisitQuasiquote(const Node: IQuasiquoteNode): Boolean; override;
- function VisitUnquote(const Node: IUnquoteNode): Boolean; override;
- function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): Boolean; override;
- function VisitMacroExpansionNode(const Node: IMacroExpansionNode): Boolean; override;
- function VisitNop(const Node: INopNode): Boolean; override;
+ // Ignored / Irrelevant for Runtime Purity
+ function VisitLambdaExpression(const Node: IAstNode): Boolean;
+ function VisitMacroDefinition(const Node: IAstNode): Boolean;
+ function VisitQuasiquote(const Node: IAstNode): Boolean;
+ function VisitUnquote(const Node: IAstNode): Boolean;
+ function VisitUnquoteSplicing(const Node: IAstNode): Boolean;
+ function VisitMacroExpansionNode(const Node: IAstNode): Boolean;
+ function VisitNop(const Node: IAstNode): Boolean;
+
+ // Pipe Support (Structural Check)
+ function VisitPipeInput(const Node: IAstNode): Boolean;
+ function VisitPipeSelectorList(const Node: IAstNode): Boolean;
+ function VisitPipeInputList(const Node: IAstNode): Boolean;
+ function VisitPipe(const Node: IAstNode): Boolean;
+
+ // Helper to check if a node is pure (handling nil gracefully)
+ function IsNodePure(const Node: IAstNode): Boolean;
+
+ protected
+ procedure SetupHandlers; override;
public
class function IsPure(const RootNode: IAstNode): Boolean;
@@ -72,80 +79,125 @@ class function TPurityAnalyzer.IsPure(const RootNode: IAstNode): Boolean;
begin
var analyzer := TPurityAnalyzer.Create;
try
- Result := analyzer.Accept(RootNode);
+ // Use the visitor's central dispatch method
+ Result := analyzer.Visit(RootNode);
finally
analyzer.Free;
end;
end;
-function TPurityAnalyzer.Accept(const Node: IAstNode): Boolean;
+function TPurityAnalyzer.IsNodePure(const Node: IAstNode): Boolean;
begin
// If a node is nil (e.g. optional else-branch), it's "nothing", which is pure.
if not Assigned(Node) then
exit(True);
- // Dispatch to specific Visit method via generic base
- Result := Node.Accept(Self).AsGeneric;
+ // Dispatch to registry
+ Result := Visit(Node);
+end;
+
+procedure TPurityAnalyzer.SetupHandlers;
+begin
+ // Core
+ Register(akConstant, VisitConstant);
+ Register(akIdentifier, VisitIdentifier);
+ Register(akKeyword, VisitKeyword);
+
+ // Lists
+ Register(akParameterList, VisitParameterList);
+ Register(akArgumentList, VisitArgumentList);
+ Register(akExpressionList, VisitExpressionList);
+ Register(akRecordFieldList, VisitRecordFieldList);
+ Register(akRecordField, VisitRecordField);
+
+ // Structural
+ Register(akIfExpression, VisitIfExpression);
+ Register(akCondExpression, VisitCondExpression);
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akFunctionCall, VisitFunctionCall);
+ Register(akMacroExpansion, VisitMacroExpansionNode);
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+ Register(akAssignment, VisitAssignment);
+ Register(akMacroDefinition, VisitMacroDefinition);
+ Register(akQuasiquote, VisitQuasiquote);
+ Register(akUnquote, VisitUnquote);
+ Register(akUnquoteSplicing, VisitUnquoteSplicing);
+ Register(akIndexer, VisitIndexer);
+ Register(akMemberAccess, VisitMemberAccess);
+ Register(akRecordLiteral, VisitRecordLiteral);
+ Register(akCreateSeries, VisitCreateSeries);
+ Register(akAddSeriesItem, VisitAddSeriesItem);
+ Register(akSeriesLength, VisitSeriesLength);
+ Register(akRecur, VisitRecurNode);
+ Register(akNop, VisitNop);
+
+ // Pipes
+ Register(akPipeInput, VisitPipeInput);
+ Register(akPipeSelectorList, VisitPipeSelectorList);
+ Register(akPipeInputList, VisitPipeInputList);
+ Register(akPipe, VisitPipe);
end;
// --- List Visitors ---
-function TPurityAnalyzer.VisitParameterList(const Node: IParameterList): Boolean;
+function TPurityAnalyzer.VisitParameterList(const Node: IAstNode): Boolean;
begin
// Declarations are pure
Result := True;
end;
-function TPurityAnalyzer.VisitArgumentList(const Node: IArgumentList): Boolean;
+function TPurityAnalyzer.VisitArgumentList(const Node: IAstNode): Boolean;
begin
- for var item in Node do
- if not Accept(item) then
+ for var item in Node.AsArgumentList do
+ if not IsNodePure(item) then
exit(False);
Result := True;
end;
-function TPurityAnalyzer.VisitExpressionList(const Node: IExpressionList): Boolean;
+function TPurityAnalyzer.VisitExpressionList(const Node: IAstNode): Boolean;
begin
- for var item in Node do
- if not Accept(item) then
+ for var item in Node.AsExpressionList do
+ if not IsNodePure(item) then
exit(False);
Result := True;
end;
-function TPurityAnalyzer.VisitRecordFieldList(const Node: IRecordFieldList): Boolean;
+function TPurityAnalyzer.VisitRecordFieldList(const Node: IAstNode): Boolean;
begin
- for var item in Node do
- if not Accept(item) then
+ for var item in Node.AsRecordFieldList do
+ if not IsNodePure(item) then
exit(False);
Result := True;
end;
-function TPurityAnalyzer.VisitRecordField(const Node: IRecordFieldNode): Boolean;
+function TPurityAnalyzer.VisitRecordField(const Node: IAstNode): Boolean;
begin
// Key is usually pure (Keyword), check Value
- Result := Accept(Node.Key) and Accept(Node.Value);
+ var F := Node.AsRecordField;
+ Result := IsNodePure(F.Key) and IsNodePure(F.Value);
end;
// --- Safe Leaves ---
-function TPurityAnalyzer.VisitConstant(const Node: IConstantNode): Boolean;
+function TPurityAnalyzer.VisitConstant(const Node: IAstNode): Boolean;
begin
Result := True;
end;
-function TPurityAnalyzer.VisitKeyword(const Node: IKeywordNode): Boolean;
+function TPurityAnalyzer.VisitKeyword(const Node: IAstNode): Boolean;
begin
Result := True;
end;
-function TPurityAnalyzer.VisitNop(const Node: INopNode): Boolean;
+function TPurityAnalyzer.VisitNop(const Node: IAstNode): Boolean;
begin
Result := True;
end;
// --- Identifier: Only local variables are safe ---
-function TPurityAnalyzer.VisitIdentifier(const Node: IIdentifierNode): Boolean;
+function TPurityAnalyzer.VisitIdentifier(const Node: IAstNode): Boolean;
begin
// We only allow access to local variables (ScopeDepth = 0).
// Accessing Parent/Upvalues (ScopeDepth > 0) makes the function state-dependent (closure state),
@@ -153,82 +205,84 @@ begin
// unless we could prove the upvalue is constant (which we don't track yet).
// Note: Parameters are also ScopeDepth=0 in the Binder logic.
- Result := (Node.Address.Kind = akLocalOrParent) and (Node.Address.ScopeDepth = 0);
+ var I := Node.AsIdentifier;
+ Result := (I.Address.Kind = akLocalOrParent) and (I.Address.ScopeDepth = 0);
end;
// --- Function Call: The Core Logic ---
-function TPurityAnalyzer.VisitFunctionCall(const Node: IFunctionCallNode): Boolean;
+function TPurityAnalyzer.VisitFunctionCall(const Node: IAstNode): Boolean;
begin
+ var C := Node.AsFunctionCall;
// 1. The target function MUST be marked as Pure (from RTL or previous inference).
- if not Node.IsTargetPure then
+ if not C.IsTargetPure then
exit(False);
// 2. All arguments must be pure expressions.
- // Delegate to ArgumentList visitor
- Result := Accept(Node.Arguments);
+ Result := IsNodePure(C.Arguments);
end;
// --- Recursion ---
-function TPurityAnalyzer.VisitRecurNode(const Node: IRecurNode): Boolean;
+function TPurityAnalyzer.VisitRecurNode(const Node: IAstNode): Boolean;
begin
// 'recur' is just control flow. It is pure if its arguments are pure.
- Result := Accept(Node.Arguments);
+ Result := IsNodePure(Node.AsRecur.Arguments);
end;
// --- Structures: Recursive Checks ---
-function TPurityAnalyzer.VisitIfExpression(const Node: IIfExpressionNode): Boolean;
+function TPurityAnalyzer.VisitIfExpression(const Node: IAstNode): Boolean;
begin
- Result := Accept(Node.Condition) and Accept(Node.ThenBranch) and Accept(Node.ElseBranch);
+ var E := Node.AsIfExpression;
+ Result := IsNodePure(E.Condition) and IsNodePure(E.ThenBranch) and IsNodePure(E.ElseBranch);
end;
-function TPurityAnalyzer.VisitCondExpression(const Node: ICondExpressionNode): Boolean;
-var
- pair: TCondPair;
+function TPurityAnalyzer.VisitCondExpression(const Node: IAstNode): Boolean;
begin
+ var E := Node.AsCondExpression;
// All conditions and all branches must be pure
- for pair in Node.Pairs do
+ for var pair in E.Pairs do
begin
- if not (Accept(pair.Condition) and Accept(pair.Branch)) then
+ if not (IsNodePure(pair.Condition) and IsNodePure(pair.Branch)) then
Exit(False);
end;
// And the Else branch
- Result := Accept(Node.ElseBranch);
+ Result := IsNodePure(E.ElseBranch);
end;
-function TPurityAnalyzer.VisitBlockExpression(const Node: IBlockExpressionNode): Boolean;
+function TPurityAnalyzer.VisitBlockExpression(const Node: IAstNode): Boolean;
begin
// Delegate to ExpressionList
- Result := Accept(Node.Expressions);
+ Result := IsNodePure(Node.AsBlockExpression.Expressions);
end;
-function TPurityAnalyzer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): Boolean;
+function TPurityAnalyzer.VisitVariableDeclaration(const Node: IAstNode): Boolean;
begin
// 'def x = ...' is locally pure if the initializer is pure.
// It mutates the local scope (stack), but that is contained within the function execution.
- Result := Accept(Node.Initializer);
+ Result := IsNodePure(Node.AsVariableDeclaration.Initializer);
end;
-function TPurityAnalyzer.VisitRecordLiteral(const Node: IRecordLiteralNode): Boolean;
+function TPurityAnalyzer.VisitRecordLiteral(const Node: IAstNode): Boolean;
begin
// Delegate to RecordFieldList
- Result := Accept(Node.Fields);
+ Result := IsNodePure(Node.AsRecordLiteral.Fields);
end;
-function TPurityAnalyzer.VisitIndexer(const Node: IIndexerNode): Boolean;
+function TPurityAnalyzer.VisitIndexer(const Node: IAstNode): Boolean;
begin
// Reading from a structure is pure if the indices/base are pure.
- Result := Accept(Node.Base) and Accept(Node.Index);
+ var I := Node.AsIndexer;
+ Result := IsNodePure(I.Base) and IsNodePure(I.Index);
end;
-function TPurityAnalyzer.VisitMemberAccess(const Node: IMemberAccessNode): Boolean;
+function TPurityAnalyzer.VisitMemberAccess(const Node: IAstNode): Boolean;
begin
- Result := Accept(Node.Base);
+ Result := IsNodePure(Node.AsMemberAccess.Base);
end;
-function TPurityAnalyzer.VisitSeriesLength(const Node: ISeriesLengthNode): Boolean;
+function TPurityAnalyzer.VisitSeriesLength(const Node: IAstNode): Boolean;
begin
// Querying length is pure.
// The series identifier check happens in VisitIdentifier.
@@ -237,19 +291,19 @@ end;
// --- Forbidden (Impure) ---
-function TPurityAnalyzer.VisitAssignment(const Node: IAssignmentNode): Boolean;
+function TPurityAnalyzer.VisitAssignment(const Node: IAstNode): Boolean;
begin
// Mutation of variables is defined as impure.
Result := False;
end;
-function TPurityAnalyzer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): Boolean;
+function TPurityAnalyzer.VisitAddSeriesItem(const Node: IAstNode): Boolean;
begin
// Mutation of a series (side effect).
Result := False;
end;
-function TPurityAnalyzer.VisitCreateSeries(const Node: ICreateSeriesNode): Boolean;
+function TPurityAnalyzer.VisitCreateSeries(const Node: IAstNode): Boolean;
begin
// Creating a NEW object is considered pure in this context,
// as it does not mutate existing global state.
@@ -258,37 +312,70 @@ end;
// --- Irrelevant / Nested (Definitions are pure, execution logic checked separately) ---
-function TPurityAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): Boolean;
+function TPurityAnalyzer.VisitLambdaExpression(const Node: IAstNode): Boolean;
begin
// Defining a function is a pure operation.
// Whether the function itself is pure when executed is determined when *that* function is compiled.
Result := True;
end;
-function TPurityAnalyzer.VisitMacroDefinition(const Node: IMacroDefinitionNode): Boolean;
+function TPurityAnalyzer.VisitMacroDefinition(const Node: IAstNode): Boolean;
begin
Result := True; // Compile-time construct
end;
-function TPurityAnalyzer.VisitQuasiquote(const Node: IQuasiquoteNode): Boolean;
+function TPurityAnalyzer.VisitQuasiquote(const Node: IAstNode): Boolean;
begin
Result := True; // Structural construction
end;
-function TPurityAnalyzer.VisitUnquote(const Node: IUnquoteNode): Boolean;
+function TPurityAnalyzer.VisitUnquote(const Node: IAstNode): Boolean;
begin
- Result := Accept(Node.Expression);
+ Result := IsNodePure(Node.AsUnquote.Expression);
end;
-function TPurityAnalyzer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): Boolean;
+function TPurityAnalyzer.VisitUnquoteSplicing(const Node: IAstNode): Boolean;
begin
- Result := Accept(Node.Expression);
+ Result := IsNodePure(Node.AsUnquoteSplicing.Expression);
end;
-function TPurityAnalyzer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): Boolean;
+function TPurityAnalyzer.VisitMacroExpansionNode(const Node: IAstNode): Boolean;
begin
// We analyze the already expanded body.
- Result := Accept(Node.ExpandedBody);
+ Result := IsNodePure(Node.AsMacroExpansion.ExpandedBody);
+end;
+
+// --- Pipe Support ---
+
+function TPurityAnalyzer.VisitPipeInput(const Node: IAstNode): Boolean;
+begin
+ var P := Node.AsPipeInput;
+ // Input node itself is declarative.
+ // But we check its parts just in case.
+ Result := IsNodePure(P.StreamSource) and IsNodePure(P.Selectors);
+end;
+
+function TPurityAnalyzer.VisitPipeSelectorList(const Node: IAstNode): Boolean;
+begin
+ for var item in Node.AsPipeSelectorList do
+ if not IsNodePure(item) then
+ exit(False);
+ Result := True;
+end;
+
+function TPurityAnalyzer.VisitPipeInputList(const Node: IAstNode): Boolean;
+begin
+ for var item in Node.AsPipeInputList do
+ if not IsNodePure(item) then
+ exit(False);
+ Result := True;
+end;
+
+function TPurityAnalyzer.VisitPipe(const Node: IAstNode): Boolean;
+begin
+ var P := Node.AsPipe;
+ // A pipe is pure if its input definitions are pure AND its transformation lambda is pure.
+ Result := IsNodePure(P.Inputs) and IsNodePure(P.Transformation);
end;
end.
diff --git a/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas b/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas
index d986a3f..562337b 100644
--- a/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas
+++ b/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas
@@ -40,11 +40,16 @@ type
FCurrentScope: TAnalysisScope;
procedure MarkDeclarationForBoxing(const AName: string);
+
+ strict private
+ // Analysis Handlers (IAstNode signature)
+ function VisitLambdaExpression(const Node: IAstNode): IAstNode;
+ function VisitIdentifier(const Node: IAstNode): IAstNode;
+ function VisitVariableDeclaration(const Node: IAstNode): IAstNode;
+
protected
- // Overridden Visit methods to perform analysis during traversal.
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
- function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
+ procedure SetupHandlers; override;
+
public
constructor Create;
destructor Destroy; override;
@@ -126,6 +131,16 @@ begin
inherited Destroy;
end;
+procedure TUpvalueAnalyzer.SetupHandlers;
+begin
+ inherited SetupHandlers; // Load default transformer logic
+
+ // Override specific handlers for analysis
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akIdentifier, VisitIdentifier);
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+end;
+
function TUpvalueAnalyzer.Execute(const ARootNode: IAstNode): IAstNode;
begin
Result := Accept(ARootNode);
@@ -167,32 +182,35 @@ begin
end;
end;
-function TUpvalueAnalyzer.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
+function TUpvalueAnalyzer.VisitIdentifier(const Node: IAstNode): IAstNode;
begin
// Check if this identifier refers to a variable from an outer scope
- MarkDeclarationForBoxing(Node.Name);
+ MarkDeclarationForBoxing(Node.AsIdentifier.Name);
// Return original node (Analysis pass only)
Result := Node;
end;
-function TUpvalueAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
+function TUpvalueAnalyzer.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
+ L: ILambdaExpressionNode;
i: Integer;
begin
+ L := Node.AsLambdaExpression;
+
// 1. Enter new analysis scope
FCurrentScope := TAnalysisScope.Create(FCurrentScope);
try
// 2. Register parameters (they mask outer variables)
// We pass 'nil' as the node because we currently don't box parameters,
// but we must ensure Resolve() finds them so we don't accidentally box a shadowed variable.
- for i := 0 to Node.Parameters.Count - 1 do
+ for i := 0 to L.Parameters.Count - 1 do
begin
- FCurrentScope.Define(Node.Parameters[i].Name, nil);
+ FCurrentScope.Define(L.Parameters[i].Name, nil);
end;
// 3. Visit Body
- Accept(Node.Body); // Recursive call
+ Accept(L.Body); // Recursive call
// Rebuild if needed (default CoW behavior)
Result := Node;
@@ -204,15 +222,19 @@ begin
end;
end;
-function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
+function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IAstNode): IAstNode;
+var
+ V: IVariableDeclarationNode;
begin
+ V := Node.AsVariableDeclaration;
+
// 1. Visit initializer first (it executes in the CURRENT scope)
- if Assigned(Node.Initializer) then
- Accept(Node.Initializer);
+ if Assigned(V.Initializer) then
+ Accept(V.Initializer);
// 2. Define the variable in the CURRENT scope
// Store the Node reference so we can add it to FBoxedDeclarations if captured.
- FCurrentScope.Define(Node.Target.AsIdentifier.Name, Node);
+ FCurrentScope.Define(V.Target.AsIdentifier.Name, V);
Result := Node;
end;
diff --git a/Src/AST/Myc.Ast.Compiler.Binder.pas b/Src/AST/Myc.Ast.Compiler.Binder.pas
index 0794425..f7b7a2f 100644
--- a/Src/AST/Myc.Ast.Compiler.Binder.pas
+++ b/Src/AST/Myc.Ast.Compiler.Binder.pas
@@ -46,16 +46,16 @@ type
function ResolveSymbol(const Name: string; out Address: TResolvedAddress): Boolean;
function CaptureUpvalue(const PhysicalAddress: TResolvedAddress): Integer;
- protected
- function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
- function VisitAssignment(const Node: IAssignmentNode): IAstNode; override;
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
+ strict private
+ // Specific Handlers (IAstNode signature)
+ function VisitIdentifier(const Node: IAstNode): IAstNode;
+ function VisitVariableDeclaration(const Node: IAstNode): IAstNode;
+ function VisitAssignment(const Node: IAstNode): IAstNode;
+ function VisitLambdaExpression(const Node: IAstNode): IAstNode;
+ function VisitFunctionCall(const Node: IAstNode): IAstNode;
- function VisitConstant(const Node: IConstantNode): IAstNode; override;
- function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
- function VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode; override;
+ protected
+ procedure SetupHandlers; override;
public
constructor Create(
@@ -114,7 +114,7 @@ constructor TAstBinder.Create(
const ALog: ICompilerLog
);
begin
- inherited Create;
+ inherited Create; // Calls SetupHandlers
FCurrentBuilder := TScope.CreateBuilder(AParentLayout);
FUpvalueStack := TObjectStack.Create(True);
FUpvalueStack.Push(TUpvalueMap.Create(TResolvedAddressComparer.Create));
@@ -133,6 +133,18 @@ begin
inherited;
end;
+procedure TAstBinder.SetupHandlers;
+begin
+ inherited SetupHandlers; // Loads default transformers (Identity, Lists, etc.)
+
+ // Override specific binding logic
+ Register(akIdentifier, VisitIdentifier);
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+ Register(akAssignment, VisitAssignment);
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akFunctionCall, VisitFunctionCall);
+end;
+
class function TAstBinder.Bind(
const ParentLayout: IScopeLayout;
const RootNode: IAstNode;
@@ -148,6 +160,7 @@ end;
function TAstBinder.Execute(const RootNode: IAstNode; out Layout: IScopeLayout): IAstNode;
begin
+ // Pre-pass to find captured variables
FBoxedDeclarations := TUpvalueAnalyzer.Analyze(RootNode);
Result := Accept(RootNode);
@@ -212,35 +225,22 @@ begin
end;
end;
-function TAstBinder.VisitConstant(const Node: IConstantNode): IAstNode;
-begin
- Result := Node;
-end;
-
-function TAstBinder.VisitKeyword(const Node: IKeywordNode): IAstNode;
-begin
- Result := Node;
-end;
-
-function TAstBinder.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
-begin
- Result := Node;
-end;
-
-function TAstBinder.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
+function TAstBinder.VisitIdentifier(const Node: IAstNode): IAstNode;
var
+ I: IIdentifierNode;
physAddr: TResolvedAddress;
upvalueIndex: Integer;
identity: INamedIdentity;
begin
- identity := Node.Identity.AsNamed;
+ I := Node.AsIdentifier;
+ identity := I.Identity.AsNamed;
- if Node.Address.Kind = akUnresolved then
+ if I.Address.Kind = akUnresolved then
begin
- if not ResolveSymbol(Node.Name, physAddr) then
+ if not ResolveSymbol(I.Name, physAddr) then
begin
if Assigned(FLog) then
- FLog.AddError(Format('Undefined identifier: "%s"', [Node.Name]), Node);
+ FLog.AddError(Format('Undefined identifier: "%s"', [I.Name]), Node);
Result := Node;
Exit;
end;
@@ -257,8 +257,9 @@ begin
Result := Node;
end;
-function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
+function TAstBinder.VisitVariableDeclaration(const Node: IAstNode): IAstNode;
var
+ V: IVariableDeclarationNode;
slot: Integer;
addr: TResolvedAddress;
newInit: IAstNode;
@@ -267,7 +268,8 @@ var
identifier: IIdentifierNode;
identIdentity: INamedIdentity;
begin
- identifier := Node.Target.AsIdentifier;
+ V := Node.AsVariableDeclaration;
+ identifier := V.Target.AsIdentifier;
identIdentity := identifier.Identity.AsNamed;
if not IsValidIdentifier(identifier.Name) then
@@ -281,8 +283,8 @@ begin
if Assigned(FLog) then
FLog.AddError(Format('Variable "%s" is already defined in this scope.', [identifier.Name]), Node);
- if Assigned(Node.Initializer) then
- Accept(Node.Initializer);
+ if Assigned(V.Initializer) then
+ Accept(V.Initializer);
Result := Node;
Exit;
@@ -291,38 +293,42 @@ begin
slot := FCurrentBuilder.Define(identifier.Name);
addr := TResolvedAddress.Create(akLocalOrParent, 0, slot);
- if Assigned(Node.Initializer) then
- newInit := Accept(Node.Initializer)
+ if Assigned(V.Initializer) then
+ newInit := Accept(V.Initializer)
else
newInit := nil;
newIdent := TAst.Identifier(identIdentity, addr, TTypes.Unknown);
- isBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
+ isBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(V);
Result := TAst.VarDecl(Node.Identity, newIdent, newInit, TTypes.Unknown, isBoxed);
if Assigned(FFunctionRegistry) and (newInit <> nil) and (newInit.Kind = akLambdaExpression) then
FFunctionRegistry.Register(addr, newInit.AsLambdaExpression);
end;
-function TAstBinder.VisitAssignment(const Node: IAssignmentNode): IAstNode;
+function TAstBinder.VisitAssignment(const Node: IAstNode): IAstNode;
var
+ A: IAssignmentNode;
newIdent: IAstNode;
newValue: IAstNode;
begin
- newIdent := Accept(Node.Target);
- newValue := Accept(Node.Value);
+ A := Node.AsAssignment;
+ // Manually accept children because we need the results for registry logic
+ newIdent := Accept(A.Target);
+ newValue := Accept(A.Value);
Result := TAst.Assign(Node.Identity, newIdent, newValue, TTypes.Unknown);
if Assigned(FFunctionRegistry) and (newValue <> nil) and (newValue.Kind = akLambdaExpression) then
begin
- if newIdent.AsIdentifier.Address.Kind = akLocalOrParent then
+ if (newIdent.Kind = akIdentifier) and (newIdent.AsIdentifier.Address.Kind = akLocalOrParent) then
FFunctionRegistry.Register(newIdent.AsIdentifier.Address, newValue.AsLambdaExpression);
end;
end;
-function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
+function TAstBinder.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
+ L: ILambdaExpressionNode;
parentBuilder: IScopeBuilder;
paramType: IStaticType;
newParams: TArray;
@@ -338,6 +344,7 @@ var
paramIdentity: INamedIdentity;
paramList: IParameterList;
begin
+ L := Node.AsLambdaExpression;
startCount := FLambdaCounter;
Inc(FLambdaCounter);
@@ -350,11 +357,11 @@ begin
try
FCurrentBuilder.Define('');
- SetLength(newParams, Node.Parameters.Count);
+ SetLength(newParams, L.Parameters.Count);
- for i := 0 to Node.Parameters.Count - 1 do
+ for i := 0 to L.Parameters.Count - 1 do
begin
- var paramNode := Node.Parameters[i];
+ var paramNode := L.Parameters[i];
var paramName := paramNode.Name;
paramIdentity := paramNode.Identity.AsNamed;
@@ -379,7 +386,7 @@ begin
newParams[i] := TAst.Identifier(paramIdentity, addr, paramType);
end;
- newBody := Accept(Node.Body);
+ newBody := Accept(L.Body);
finalLayout := FCurrentBuilder.Build;
sortedPairs := capturedMap.ToArray;
@@ -408,28 +415,34 @@ begin
end;
hasNested := FLambdaCounter > (startCount + 1);
+ paramList := TParameterList.Create(newParams, L.Parameters.Identity);
- paramList := TParameterList.Create(newParams, Node.Parameters.Identity);
-
- Result := TAst.LambdaExpr(Node.Identity, paramList, newBody, finalLayout, nil, upvaluesList, hasNested, Node.IsPure);
+ Result := TAst.LambdaExpr(Node.Identity, paramList, newBody, finalLayout, nil, upvaluesList, hasNested, L.IsPure);
end;
-function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
+function TAstBinder.VisitFunctionCall(const Node: IAstNode): IAstNode;
+var
+ C: IFunctionCallNode;
begin
- if Node.Callee.Kind = akKeyword then
+ C := Node.AsFunctionCall;
+ // 1. Keyword as Function Logic
+ if C.Callee.Kind = akKeyword then
begin
- if Node.Arguments.Count <> 1 then
+ if C.Arguments.Count <> 1 then
begin
if Assigned(FLog) then
- FLog.AddError(Format('Keyword access :%s requires exactly one argument.', [Node.Callee.AsKeyword.Value.Name]), Node);
+ FLog.AddError(Format('Keyword access :%s requires exactly one argument.', [C.Callee.AsKeyword.Value.Name]), Node);
end
else
begin
- var memberAccess := TAst.MemberAccess(Node.Identity, Accept(Node.Arguments[0]), Node.Callee.AsKeyword);
+ var memberAccess := TAst.MemberAccess(Node.Identity, Accept(C.Arguments[0]), C.Callee.AsKeyword);
Result := memberAccess;
exit;
end;
end;
+
+ // 2. Delegate to standard AST transformation (recurse on callee and args)
+ // This reuses logic from TAstTransformer!
Result := inherited VisitFunctionCall(Node);
end;
diff --git a/Src/AST/Myc.Ast.Compiler.Macros.pas b/Src/AST/Myc.Ast.Compiler.Macros.pas
index 0aeedfa..ab95509 100644
--- a/Src/AST/Myc.Ast.Compiler.Macros.pas
+++ b/Src/AST/Myc.Ast.Compiler.Macros.pas
@@ -15,42 +15,39 @@ uses
Myc.Ast;
type
- // Exception specific to macro expansion errors
EMacroException = class(EAstException);
IAstMacroExpander = interface(IAstVisitor)
function Execute(const RootNode: IAstNode): IAstNode;
end;
- // Callback used by TMacroExpander to request full compilation and execution of macro arguments.
TMacroEvaluatorProc = reference to function(const Scope: IExecutionScope; const Node: IAstNode): TDataValue;
- // Handles the expansion of the macro body template (Quasiquotes/Unquotes).
+ // Handles body expansion (Template instantiation)
TExpansionVisitor = class(TAstTransformer)
- private
+ strict private
FMacroEvaluator: TMacroEvaluatorProc;
FMacroScope: IExecutionScope;
FRenameMap: TDictionary;
class var
FGensymCounter: Int64;
- // Helper to handle unquote-splicing (~@) within lists.
- // Takes a source list (e.g., Arguments) and returns a flattened array for the new node.
function TransformAndSpliceNodes(const ANodes: INodeList): TArray;
function Gensym(const ABaseName: string): string;
+
+ // Custom Logic Handlers (IAstNode signature)
+ function VisitUnquote(const Node: IAstNode): IAstNode;
+ function VisitUnquoteSplicing(const Node: IAstNode): IAstNode;
+ function VisitFunctionCall(const Node: IAstNode): IAstNode;
+ function VisitBlockExpression(const Node: IAstNode): IAstNode;
+ function VisitRecordLiteral(const Node: IAstNode): IAstNode;
+ function VisitVariableDeclaration(const Node: IAstNode): IAstNode;
+ function VisitLambdaExpression(const Node: IAstNode): IAstNode;
+ function VisitIdentifier(const Node: IAstNode): IAstNode;
+
protected
- function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
- function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override;
+ procedure SetupHandlers; override;
- // We override these container visits to handle Splicing manually,
- // bypassing the standard 1:1 list visitor.
- function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
- function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
-
- function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
- function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
public
constructor Create(const AMacroScope: IExecutionScope; const AMacroEvaluator: TMacroEvaluatorProc);
destructor Destroy; override;
@@ -72,9 +69,9 @@ type
property Parent: IMacroRegistry read GetParent;
end;
- // Handles the expansion of macro calls within the AST.
+ // Handles macro calls in AST
TMacroExpander = class(TAstTransformer, IAstMacroExpander)
- private
+ strict private
FInitialScope: IExecutionScope;
FCurrentMacroRegistry: IMacroRegistry;
FMacroEvaluator: TMacroEvaluatorProc;
@@ -82,16 +79,16 @@ type
procedure EnterMacroScope;
procedure ExitMacroScope;
+ function VisitMacroDefinition(const Node: IAstNode): IAstNode;
+ function VisitFunctionCall(const Node: IAstNode): IAstNode;
+ function VisitQuasiquote(const Node: IAstNode): IAstNode;
+ function VisitUnquote(const Node: IAstNode): IAstNode;
+ function VisitUnquoteSplicing(const Node: IAstNode): IAstNode;
+ function VisitBlockExpression(const Node: IAstNode): IAstNode;
+ function VisitLambdaExpression(const Node: IAstNode): IAstNode;
+
protected
- function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
-
- function VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; override;
- function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
- function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override;
-
- function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
+ procedure SetupHandlers; override;
public
constructor Create(
@@ -111,7 +108,6 @@ type
): IAstNode; static;
end;
- // Implementation of the macro registry interface defined in Myc.Ast.Environment.
TMacroRegistry = class(TInterfacedObject, IMacroRegistry)
private
FParent: IMacroRegistry;
@@ -148,6 +144,22 @@ begin
inherited;
end;
+procedure TExpansionVisitor.SetupHandlers;
+begin
+ inherited SetupHandlers; // Defaults
+
+ // Register custom handlers
+ Register(akUnquote, VisitUnquote);
+ Register(akUnquoteSplicing, VisitUnquoteSplicing);
+
+ Register(akFunctionCall, VisitFunctionCall);
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akRecordLiteral, VisitRecordLiteral);
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akIdentifier, VisitIdentifier);
+end;
+
function TExpansionVisitor.Gensym(const ABaseName: string): string;
begin
if not FRenameMap.TryGetValue(ABaseName, Result) then
@@ -189,7 +201,6 @@ begin
begin
var spliceExpr := node.AsUnquoteSplicing.Expression;
var evaluatedSpliceValue: TDataValue;
-
try
evaluatedSpliceValue := FMacroEvaluator(FMacroScope, spliceExpr);
except
@@ -202,36 +213,20 @@ begin
if (not evaluatedSpliceValue.IsVoid) and (evaluatedSpliceValue.Kind = vkInterface) then
begin
nodeToSplice := evaluatedSpliceValue.AsIntf;
-
- // Splicing implies the value must be a list-like AST structure (Block or just a List Node)
- // If the evaluated value is an IArgumentList, IExpressionList etc., we flatten it.
if nodeToSplice.Kind = akExpressionList then
- begin
for var subItem in nodeToSplice.AsExpressionList do
- newList.Add(subItem);
- end
+ newList.Add(subItem)
else if nodeToSplice.Kind = akArgumentList then
- begin
for var subItem in nodeToSplice.AsArgumentList do
- newList.Add(subItem);
- end
+ newList.Add(subItem)
else if nodeToSplice.Kind = akBlockExpression then
- begin
- // Splice content of block
for var subItem in nodeToSplice.AsBlockExpression.Expressions do
- newList.Add(subItem);
- end
+ newList.Add(subItem)
else
- begin
- // Treat as single item
newList.Add(nodeToSplice);
- end;
end
else if (not evaluatedSpliceValue.IsVoid) then
- begin
- // Use Splice Node Identity for the new Constant
newList.Add(TAst.Constant(evaluatedSpliceValue, node.Identity.Location));
- end;
end
else
begin
@@ -246,76 +241,69 @@ begin
end;
end;
-function TExpansionVisitor.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
+function TExpansionVisitor.VisitIdentifier(const Node: IAstNode): IAstNode;
var
+ I: IIdentifierNode;
newName: string;
begin
- // Apply hygienic renaming
- if FRenameMap.TryGetValue(Node.Name, newName) then
+ I := Node.AsIdentifier;
+ if FRenameMap.TryGetValue(I.Name, newName) then
begin
- // Create new Identifier with new Name but preserve Location from original Node
- Result := TAst.Identifier(newName, Node.Identity.Location);
+ Result := TAst.Identifier(newName, I.Identity.Location);
exit;
end;
Result := Node;
end;
-function TExpansionVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
+function TExpansionVisitor.VisitVariableDeclaration(const Node: IAstNode): IAstNode;
var
+ V: IVariableDeclarationNode;
newTarget, newInit: IAstNode;
newName: string;
begin
- newInit := Accept(Node.Initializer);
-
- if Node.Target.Kind = akIdentifier then
+ V := Node.AsVariableDeclaration;
+ newInit := Accept(V.Initializer);
+ if V.Target.Kind = akIdentifier then
begin
- newName := Gensym(Node.Target.AsIdentifier.Name);
- // Use location from original target
- newTarget := TAst.Identifier(newName, Node.Target.Identity.Location);
+ newName := Gensym(V.Target.AsIdentifier.Name);
+ newTarget := TAst.Identifier(newName, V.Target.Identity.Location);
end
else
- begin
- newTarget := Accept(Node.Target);
- end;
+ newTarget := Accept(V.Target);
Result := TAst.VarDecl(Node.Identity, newTarget, newInit, TTypes.Unknown, False);
end;
-function TExpansionVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
+function TExpansionVisitor.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
+ L: ILambdaExpressionNode;
newParams: TArray;
newBody: IAstNode;
newName: string;
i: Integer;
- paramList: IParameterList;
begin
- SetLength(newParams, Node.Parameters.Count);
- for i := 0 to Node.Parameters.Count - 1 do
+ L := Node.AsLambdaExpression;
+ SetLength(newParams, L.Parameters.Count);
+ for i := 0 to L.Parameters.Count - 1 do
begin
- var param := Node.Parameters[i];
+ var param := L.Parameters[i];
newName := Gensym(param.Name);
- // Use location from original param
newParams[i] := TAst.Identifier(newName, param.Identity.Location);
end;
- newBody := Accept(Node.Body);
-
- // Rebuild: Wrap array in TParameterList with original Identity
- paramList := TParameterList.Create(newParams, Node.Parameters.Identity);
-
- // Use transformation overload
- Result := TAst.LambdaExpr(Node.Identity, paramList, newBody);
+ newBody := Accept(L.Body);
+ Result := TAst.LambdaExpr(Node.Identity, TParameterList.Create(newParams, L.Parameters.Identity), newBody);
end;
-function TExpansionVisitor.VisitUnquote(const Node: IUnquoteNode): IAstNode;
+function TExpansionVisitor.VisitUnquote(const Node: IAstNode): IAstNode;
var
+ U: IUnquoteNode;
value: TDataValue;
expr: IAstNode;
addr: TResolvedAddress;
begin
- expr := Node.Expression;
-
- // Optimization: If unquote refers to a local macro variable directly, try to get it.
+ U := Node.AsUnquote;
+ expr := U.Expression;
if expr.Kind = akIdentifier then
begin
addr := FMacroScope.Resolve(expr.AsIdentifier.Name);
@@ -323,10 +311,7 @@ begin
begin
var argValue := FMacroScope.Values[addr];
if argValue.Kind = vkInterface then
- begin
- Result := argValue.AsIntf;
- exit;
- end;
+ exit(argValue.AsIntf);
end;
end;
@@ -340,57 +325,45 @@ begin
end;
if value.Kind = vkInterface then
- begin
- Result := value.AsIntf;
- exit;
- end;
+ exit(value.AsIntf);
if value.Kind in [vkScalar, vkText, vkVoid] then
- // Create Constant node, inheriting location from the Unquote node
Result := TAst.Constant(value, Node.Identity.Location)
else
raise EMacroException.CreateFmt('Cannot unquote complex runtime value of type %s at compile time.', [value.Kind.ToString]);
end;
-function TExpansionVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
+function TExpansionVisitor.VisitUnquoteSplicing(const Node: IAstNode): IAstNode;
begin
raise EMacroException.Create('Unquote-splicing (`~@`) can only appear inside a list form.');
end;
-function TExpansionVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
+function TExpansionVisitor.VisitFunctionCall(const Node: IAstNode): IAstNode;
var
+ C: IFunctionCallNode;
newArgs: TArray;
transformedCallee: IAstNode;
- argList: IArgumentList;
begin
- transformedCallee := Self.Accept(Node.Callee);
-
- // Use splicing aware transformation for arguments (returns TArray)
- newArgs := TransformAndSpliceNodes(Node.Arguments);
-
- // Wrap in List container to use Transformation overload
- argList := TArgumentList.Create(newArgs, Node.Arguments.Identity);
-
- Result := TAst.FunctionCall(Node.Identity, transformedCallee, argList);
+ C := Node.AsFunctionCall;
+ transformedCallee := Self.Accept(C.Callee);
+ newArgs := TransformAndSpliceNodes(C.Arguments);
+ Result := TAst.FunctionCall(Node.Identity, transformedCallee, TArgumentList.Create(newArgs, C.Arguments.Identity));
end;
-function TExpansionVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
+function TExpansionVisitor.VisitBlockExpression(const Node: IAstNode): IAstNode;
var
+ B: IBlockExpressionNode;
newExprs: TArray;
- exprList: IExpressionList;
begin
- // Use splicing aware transformation for block expressions
- newExprs := TransformAndSpliceNodes(Node.Expressions);
-
- // Wrap in List
- exprList := TExpressionList.Create(newExprs, Node.Expressions.Identity);
-
- Result := TAst.Block(Node.Identity, exprList);
+ B := Node.AsBlockExpression;
+ newExprs := TransformAndSpliceNodes(B.Expressions);
+ Result := TAst.Block(Node.Identity, TExpressionList.Create(newExprs, B.Expressions.Identity));
end;
-function TExpansionVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
+function TExpansionVisitor.VisitRecordLiteral(const Node: IAstNode): IAstNode;
begin
- // For now, standard transformation for records (no splicing key-values yet)
+ // Delegate to standard transformer to process fields recursively
+ // This calls inherited VisitRecordLiteral(IAstNode)
Result := inherited VisitRecordLiteral(Node);
end;
@@ -413,6 +386,21 @@ begin
inherited Destroy;
end;
+procedure TMacroExpander.SetupHandlers;
+begin
+ inherited SetupHandlers; // Defaults
+
+ Register(akMacroDefinition, VisitMacroDefinition);
+ Register(akFunctionCall, VisitFunctionCall);
+ Register(akQuasiquote, VisitQuasiquote);
+ Register(akUnquote, VisitUnquote);
+ Register(akUnquoteSplicing, VisitUnquoteSplicing);
+
+ // Wrappers for scope management
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akLambdaExpression, VisitLambdaExpression);
+end;
+
procedure TMacroExpander.EnterMacroScope;
begin
FCurrentMacroRegistry := FCurrentMacroRegistry.CreateChildRegistry;
@@ -441,75 +429,81 @@ begin
Result := TAst.Block([], nil);
end;
-function TMacroExpander.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
+function TMacroExpander.VisitBlockExpression(const Node: IAstNode): IAstNode;
begin
EnterMacroScope;
try
+ // Delegate to standard recursive transform
Result := inherited VisitBlockExpression(Node);
finally
ExitMacroScope;
end;
end;
-function TMacroExpander.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
+function TMacroExpander.VisitLambdaExpression(const Node: IAstNode): IAstNode;
begin
EnterMacroScope;
try
+ // Delegate to standard recursive transform
Result := inherited VisitLambdaExpression(Node);
finally
ExitMacroScope;
end;
end;
-function TMacroExpander.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
+function TMacroExpander.VisitMacroDefinition(const Node: IAstNode): IAstNode;
begin
- FCurrentMacroRegistry.Define(Node);
+ FCurrentMacroRegistry.Define(Node.AsMacroDefinition);
Result := Node;
end;
-function TMacroExpander.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
+function TMacroExpander.VisitFunctionCall(const Node: IAstNode): IAstNode;
var
+ C: IFunctionCallNode;
calleeIdentifier: IIdentifierNode;
macroDef: IMacroDefinitionNode;
i: Integer;
begin
- if Node.Callee.Kind <> akIdentifier then
- exit(inherited VisitFunctionCall(Node));
+ C := Node.AsFunctionCall;
+ if C.Callee.Kind = akIdentifier then
+ begin
+ calleeIdentifier := C.Callee.AsIdentifier;
+ macroDef := FCurrentMacroRegistry.Find(calleeIdentifier.Name);
- calleeIdentifier := Node.Callee.AsIdentifier;
- macroDef := FCurrentMacroRegistry.Find(calleeIdentifier.Name);
- if macroDef = nil then
- exit(inherited VisitFunctionCall(Node));
+ if macroDef <> nil then
+ begin
+ var expansionScope := TScope.CreateScope(FInitialScope, nil, nil);
+ var params := macroDef.Parameters;
- var expansionScope := TScope.CreateScope(FInitialScope, nil, nil);
+ if C.Arguments.Count <> params.Count then
+ raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, params.Count]);
- var params := macroDef.Parameters;
- if Node.Arguments.Count <> params.Count then
- raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, params.Count]);
+ for i := 0 to params.Count - 1 do
+ expansionScope.Define(params[i].Name, TDataValue.FromIntf(C.Arguments[i]));
- for i := 0 to params.Count - 1 do
- expansionScope.Define(params[i].Name, TDataValue.FromIntf(Node.Arguments[i]));
+ var expandedBody := TExpansionVisitor.Expand(expansionScope, macroDef.Body.AsQuasiquote.Expression, FMacroEvaluator);
+ var macroNode := TAst.MacroExpansionNode(Node.Identity, C, expandedBody);
- var expandedBody := TExpansionVisitor.Expand(expansionScope, macroDef.Body.AsQuasiquote.Expression, FMacroEvaluator);
+ Result := Self.Accept(macroNode);
+ exit;
+ end;
+ end;
- // The MacroExpansionNode wraps the expansion, preserving the original call's identity (Source Location)
- // for better error reporting.
- var macroNode := TAst.MacroExpansionNode(Node.Identity, Node, expandedBody);
-
- Result := Self.Accept(macroNode);
+ // Standard recursion for non-macro calls
+ Result := inherited VisitFunctionCall(Node);
end;
-function TMacroExpander.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode;
+function TMacroExpander.VisitQuasiquote(const Node: IAstNode): IAstNode;
begin
Result := Node;
end;
-function TMacroExpander.VisitUnquote(const Node: IUnquoteNode): IAstNode;
+function TMacroExpander.VisitUnquote(const Node: IAstNode): IAstNode;
begin
raise EMacroException.Create('Unquote (`~`) can only be used inside a quasiquote.');
end;
-function TMacroExpander.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
+function TMacroExpander.VisitUnquoteSplicing(const Node: IAstNode): IAstNode;
begin
raise EMacroException.Create('Unquote-splicing (`~@`) can only be used inside a quasiquote.');
end;
diff --git a/Src/AST/Myc.Ast.Compiler.Specializer.pas b/Src/AST/Myc.Ast.Compiler.Specializer.pas
index 48f0b98..9bd01fc 100644
--- a/Src/AST/Myc.Ast.Compiler.Specializer.pas
+++ b/Src/AST/Myc.Ast.Compiler.Specializer.pas
@@ -16,7 +16,7 @@ uses
Myc.Ast.Compiler.Binder;
type
- // Exception specific to specialization errors (e.g. internal compilation failures)
+ // Exception specific to specialization errors
ESpecializerException = class(EAstException);
IAstSpecializer = interface(IAstVisitor)
@@ -29,7 +29,7 @@ type
public
Address: TResolvedAddress;
ArgTypes: TArray;
- Func: TDataValue.TFunc;
+ Func: TDataValue.TFunc; // Usually nil in key, but part of structure if needed
constructor Create(const AAddress: TResolvedAddress; const AArgTypes: TArray);
end;
@@ -39,10 +39,9 @@ type
end;
// This transformer runs *after* TypeChecker.
- // It specializes all statically resolvable function calls (RTL and user-defined)
- // by replacing them with nodes that have a direct StaticTarget.
- // It propagates Purity information but does NOT perform Constant Folding yet.
+ // It specializes all statically resolvable function calls.
TStaticSpecializer = class(TAstTransformer, IAstSpecializer)
+ public
type
TCompileFunc = reference to function(const Node: IFunctionDefinition; const ArgTypes: TArray): TCompiledFunction;
private
@@ -52,8 +51,12 @@ type
function GetStaticRtlFunction(const AName: string; const AArgTypes: TArray): TSpecializedMethod;
+ strict private
+ // Specialization Handler (IAstNode signature)
+ function VisitFunctionCall(const Node: IAstNode): IAstNode;
+
protected
- function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
+ procedure SetupHandlers; override;
public
constructor Create(
@@ -89,13 +92,19 @@ begin
raise ESpecializerException.Create('MonomorphCache cannot be nil.');
if not Assigned(AFunctionRegistry) then
raise ESpecializerException.Create('FunctionRegistry cannot be nil.');
- // CompileFunc can be nil if no recursion is supported, but usually required.
FMonomorphCache := AMonomorphCache;
FFunctionRegistry := AFunctionRegistry;
FCompileFunc := ACompileFunc;
end;
+procedure TStaticSpecializer.SetupHandlers;
+begin
+ inherited SetupHandlers; // Load default transformations
+ // Override FunctionCall logic
+ Register(akFunctionCall, VisitFunctionCall);
+end;
+
class function TStaticSpecializer.Specialize(
const RootNode: IAstNode;
const AMonomorphCache: IMonomorphCache;
@@ -119,8 +128,10 @@ begin
Result := TRtlRegistry.GetStaticSpecialization(AName, AArgTypes);
end;
-function TStaticSpecializer.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
+function TStaticSpecializer.VisitFunctionCall(const Node: IAstNode): IAstNode;
var
+ C: IFunctionCallNode;
+ newCall: IFunctionCallNode;
newCallee: IAstNode;
newArgsList: IArgumentList;
i: Integer;
@@ -132,18 +143,18 @@ var
specializedMethod: TSpecializedMethod;
funcDef: IFunctionDefinition;
begin
- // 1. Specialize children first (bottom-up)
- newCallee := Accept(Node.Callee);
- // Arguments are now visited as a List, returning an IArgumentList
- newArgsList := Accept(Node.Arguments).AsArgumentList;
+ C := Node.AsFunctionCall;
+
+ // 1. Specialize children first (bottom-up) by calling inherited
+ // inherited VisitFunctionCall returns an IAstNode (which is a new IFunctionCallNode if changed)
+ newCall := inherited VisitFunctionCall(Node).AsFunctionCall;
+ newCallee := newCall.Callee;
+ newArgsList := newCall.Arguments;
// 2. Check if this call is a candidate for specialization
if newCallee.Kind <> akIdentifier then
begin
- if (newCallee = Node.Callee) and (newArgsList = Node.Arguments) then
- Result := Node
- else
- Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, Node.StaticType, Node.IsTailCall, nil);
+ Result := newCall;
exit;
end;
@@ -165,10 +176,7 @@ begin
if not allTypesKnown then
begin
- if (newCallee = Node.Callee) and (newArgsList = Node.Arguments) then
- Result := Node
- else
- Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, Node.StaticType, Node.IsTailCall, nil);
+ Result := newCall;
exit;
end;
@@ -186,7 +194,7 @@ begin
newCallee,
newArgsList,
specializedMethod.ReturnType,
- Node.IsTailCall,
+ C.IsTailCall,
specializedMethod.Target,
specializedMethod.IsPure
);
@@ -206,7 +214,7 @@ begin
newCallee,
newArgsList,
specializedMethod.ReturnType,
- Node.IsTailCall,
+ C.IsTailCall,
specializedMethod.Target,
specializedMethod.IsPure
);
@@ -224,7 +232,7 @@ begin
var lambdaDef := funcDef.AsLambdaExpression;
if (Length(lambdaDef.Upvalues) > 0) or (lambdaDef.HasNestedLambdas) then
begin
- Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, Node.StaticType, Node.IsTailCall, nil);
+ Result := newCall;
exit;
end;
end;
@@ -245,15 +253,12 @@ begin
FMonomorphCache.Add(key, specializedMethod);
// 6c. Return the new node
- Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, returnType, Node.IsTailCall, compiled.Func, compiled.IsPure);
+ Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, returnType, C.IsTailCall, compiled.Func, compiled.IsPure);
exit;
end;
// 7. Fallback: Not RTL, Not User-Code -> Dynamic
- if (newCallee = Node.Callee) and (newArgsList = Node.Arguments) then
- Result := Node
- else
- Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, Node.StaticType, Node.IsTailCall, nil);
+ Result := newCall;
end;
{ TMonoCacheKey }
@@ -262,6 +267,7 @@ constructor TMonoCacheKey.Create(const AAddress: TResolvedAddress; const AArgTyp
begin
Address := AAddress;
ArgTypes := AArgTypes;
+ Func := nil;
end;
end.
diff --git a/Src/AST/Myc.Ast.Compiler.TCO.pas b/Src/AST/Myc.Ast.Compiler.TCO.pas
index 46e52e7..97ba728 100644
--- a/Src/AST/Myc.Ast.Compiler.TCO.pas
+++ b/Src/AST/Myc.Ast.Compiler.TCO.pas
@@ -28,18 +28,23 @@ type
private
FIsTailStack: TStack;
FNextIsTail: Boolean;
- protected
- function Accept(const Node: IAstNode): IAstNode; override;
- // Overrides for TCO propagation
- function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
- function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
- function VisitCondExpression(const Node: ICondExpressionNode): IAstNode; override; // Replaces Ternary
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
- function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
- function VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; override;
- // The core TCO logic
- function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
+ strict private
+ // Typed Handlers (IAstNode signature)
+ function VisitBlockExpression(const Node: IAstNode): IAstNode;
+ function VisitIfExpression(const Node: IAstNode): IAstNode;
+ function VisitCondExpression(const Node: IAstNode): IAstNode;
+ function VisitLambdaExpression(const Node: IAstNode): IAstNode;
+ function VisitRecurNode(const Node: IAstNode): IAstNode;
+ function VisitMacroExpansionNode(const Node: IAstNode): IAstNode;
+ function VisitFunctionCall(const Node: IAstNode): IAstNode;
+
+ protected
+ procedure SetupHandlers; override;
+
+ // Intercept dispatch to manage the TCO Stack state
+ function Accept(const Node: IAstNode): IAstNode; override;
+
public
constructor Create;
destructor Destroy; override;
@@ -65,6 +70,19 @@ begin
inherited;
end;
+procedure TAstTCO.SetupHandlers;
+begin
+ inherited SetupHandlers; // Defaults
+
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akIfExpression, VisitIfExpression);
+ Register(akCondExpression, VisitCondExpression);
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akRecur, VisitRecurNode);
+ Register(akMacroExpansion, VisitMacroExpansionNode);
+ Register(akFunctionCall, VisitFunctionCall);
+end;
+
class function TAstTCO.Optimize(const RootNode: IAstNode): IAstNode;
begin
var optimizer := TAstTCO.Create as IAstTCO;
@@ -81,22 +99,22 @@ end;
function TAstTCO.Accept(const Node: IAstNode): IAstNode;
begin
if (not Assigned(Node)) then
- begin
- Result := nil;
- exit;
- end;
+ exit(nil);
+ // Push current context state before visiting children
FIsTailStack.Push(FNextIsTail);
try
- // Call inherited Accept
+ // Dispatch to handler (which will call VisitXyz)
Result := inherited Accept(Node);
finally
+ // Restore context state
FNextIsTail := FIsTailStack.Pop;
end;
end;
-function TAstTCO.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
+function TAstTCO.VisitBlockExpression(const Node: IAstNode): IAstNode;
var
+ B: IBlockExpressionNode;
isContextTail: Boolean;
newExprs: TArray;
exprsList: IExpressionList;
@@ -104,8 +122,9 @@ var
item, newItem: IAstNode;
hasChanged: Boolean;
begin
+ B := Node.AsBlockExpression;
isContextTail := FIsTailStack.Peek;
- exprsList := Node.Expressions;
+ exprsList := B.Expressions;
SetLength(newExprs, exprsList.Count);
hasChanged := False;
@@ -129,39 +148,36 @@ begin
if not hasChanged then
Result := Node
else
- begin
- // Wrap array in List container
- var newList := TExpressionList.Create(newExprs, Node.Expressions.Identity);
- // CoW: Reuse Identity
- Result := TAst.Block(Node.Identity, newList, Node.StaticType);
- end;
+ Result := TAst.Block(Node.Identity, TExpressionList.Create(newExprs, B.Expressions.Identity), B.StaticType);
end;
-function TAstTCO.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
+function TAstTCO.VisitIfExpression(const Node: IAstNode): IAstNode;
var
+ I: IIfExpressionNode;
isContextTail: Boolean;
newCond, newThen, newElse: IAstNode;
begin
+ I := Node.AsIfExpression;
isContextTail := FIsTailStack.Peek;
// Condition is never in tail position
FNextIsTail := False;
- newCond := Accept(Node.Condition);
+ newCond := Accept(I.Condition);
// Then/Else branches ARE in tail position if the IfExpr is
FNextIsTail := isContextTail;
- newThen := Accept(Node.ThenBranch);
- newElse := Accept(Node.ElseBranch);
+ newThen := Accept(I.ThenBranch);
+ newElse := Accept(I.ElseBranch);
- if (newCond = Node.Condition) and (newThen = Node.ThenBranch) and (newElse = Node.ElseBranch) then
+ if (newCond = I.Condition) and (newThen = I.ThenBranch) and (newElse = I.ElseBranch) then
Result := Node
else
- // CoW: Reuse Identity
- Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, Node.StaticType);
+ Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, I.StaticType);
end;
-function TAstTCO.VisitCondExpression(const Node: ICondExpressionNode): IAstNode;
+function TAstTCO.VisitCondExpression(const Node: IAstNode): IAstNode;
var
+ C: ICondExpressionNode;
isContextTail: Boolean;
hasChanged: Boolean;
i: Integer;
@@ -169,143 +185,133 @@ var
newElse: IAstNode;
newCond, newBranch: IAstNode;
begin
+ C := Node.AsCondExpression;
isContextTail := FIsTailStack.Peek;
hasChanged := False;
- SetLength(newPairs, Length(Node.Pairs));
+ SetLength(newPairs, Length(C.Pairs));
- for i := 0 to High(Node.Pairs) do
+ for i := 0 to High(C.Pairs) do
begin
// 1. Condition is never in tail position
FNextIsTail := False;
- newCond := Accept(Node.Pairs[i].Condition);
+ newCond := Accept(C.Pairs[i].Condition);
// 2. Branch IS in tail position (if CondExpr is)
FNextIsTail := isContextTail;
- newBranch := Accept(Node.Pairs[i].Branch);
+ newBranch := Accept(C.Pairs[i].Branch);
newPairs[i] := TCondPair.Create(newCond, newBranch);
- if (newCond <> Node.Pairs[i].Condition) or (newBranch <> Node.Pairs[i].Branch) then
+ if (newCond <> C.Pairs[i].Condition) or (newBranch <> C.Pairs[i].Branch) then
hasChanged := True;
end;
// 3. Else Branch IS in tail position
FNextIsTail := isContextTail;
- newElse := Accept(Node.ElseBranch);
+ newElse := Accept(C.ElseBranch);
- if newElse <> Node.ElseBranch then
+ if newElse <> C.ElseBranch then
hasChanged := True;
if not hasChanged then
Result := Node
else
- Result := TAst.CondExpr(Node.Identity, newPairs, newElse, Node.StaticType);
+ Result := TAst.CondExpr(Node.Identity, newPairs, newElse, C.StaticType);
end;
-function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
+function TAstTCO.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
+ L: ILambdaExpressionNode;
newParams: IParameterList;
newBody: IAstNode;
begin
+ L := Node.AsLambdaExpression;
// Parameters are not in tail position
FNextIsTail := False;
- // Visit list node to handle potential transformations in children
- newParams := Accept(Node.Parameters).AsParameterList;
+ newParams := Accept(L.Parameters).AsParameterList;
// The body of a lambda is *always* a tail position (relative to the lambda execution)
FNextIsTail := True;
- newBody := Accept(Node.Body);
+ newBody := Accept(L.Body);
- if (newParams = Node.Parameters) and (newBody = Node.Body) then
+ if (newParams = L.Parameters) and (newBody = L.Body) then
Result := Node
else
begin
- // Rebuild using factory.
- // CoW: Reuse Identity. Factory expects IParameterList now.
Result :=
TAst.LambdaExpr(
Node.Identity,
newParams,
newBody,
- Node.Layout,
- Node.Descriptor,
- Node.Upvalues,
- Node.HasNestedLambdas,
- Node.IsPure,
- Node.StaticType
+ L.Layout,
+ L.Descriptor,
+ L.Upvalues,
+ L.HasNestedLambdas,
+ L.IsPure,
+ L.StaticType
);
end;
end;
-function TAstTCO.VisitRecurNode(const Node: IRecurNode): IAstNode;
+function TAstTCO.VisitRecurNode(const Node: IAstNode): IAstNode;
var
+ R: IRecurNode;
newArgsList: IArgumentList;
begin
+ R := Node.AsRecur;
if not FIsTailStack.Peek then
raise EOptimizerException.Create('''recur'' can only be used in a tail position.');
// Arguments are not in tail position
FNextIsTail := False;
- // Visit the list node. Accept() ensures children are visited via TAstTransformer.VisitArgumentList
- newArgsList := Accept(Node.Arguments).AsArgumentList;
+ newArgsList := Accept(R.Arguments).AsArgumentList;
- if newArgsList = Node.Arguments then
+ if newArgsList = R.Arguments then
Result := Node
else
- // CoW: Reuse Identity. Factory expects IArgumentList.
- Result := TAst.Recur(Node.Identity, newArgsList, Node.StaticType);
+ Result := TAst.Recur(Node.Identity, newArgsList, R.StaticType);
end;
-function TAstTCO.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
+function TAstTCO.VisitMacroExpansionNode(const Node: IAstNode): IAstNode;
var
+ M: IMacroExpansionNode;
newBody: IAstNode;
begin
+ M := Node.AsMacroExpansion;
// Propagate tail call status to the expanded body
FNextIsTail := FIsTailStack.Peek;
- newBody := Accept(Node.ExpandedBody);
+ newBody := Accept(M.ExpandedBody);
- if newBody = Node.ExpandedBody then
+ if newBody = M.ExpandedBody then
Result := Node
else
- // CoW: Reuse Identity
- Result := TAst.MacroExpansionNode(Node.Identity, Node.CallNode, newBody);
+ Result := TAst.MacroExpansionNode(Node.Identity, M.CallNode, newBody);
end;
-function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
+function TAstTCO.VisitFunctionCall(const Node: IAstNode): IAstNode;
var
+ C: IFunctionCallNode;
isTailCall: Boolean;
newCallee: IAstNode;
newArgsList: IArgumentList;
begin
+ C := Node.AsFunctionCall;
isTailCall := FIsTailStack.Peek;
// Callee/Arguments are not in tail position
FNextIsTail := False;
- newCallee := Accept(Node.Callee);
+ newCallee := Accept(C.Callee);
+ newArgsList := Accept(C.Arguments).AsArgumentList;
- // Visit the arguments list
- newArgsList := Accept(Node.Arguments).AsArgumentList;
-
- // CoW check: Create a new node only if children changed OR IsTailCall needs update
- if (newCallee = Node.Callee) and (newArgsList = Node.Arguments) and (isTailCall = Node.IsTailCall) then
+ if (newCallee = C.Callee) and (newArgsList = C.Arguments) and (isTailCall = C.IsTailCall) then
begin
Result := Node;
exit;
end;
- // Use factory to create new node, passing the *new* TCO status
- // CoW: Reuse Identity. Factory expects IArgumentList.
- Result :=
- TAst.FunctionCall(
- Node.Identity,
- newCallee,
- newArgsList,
- Node.StaticType,
- isTailCall,
- Node.StaticTarget, // Preserve the static target from Specializer
- Node.IsTargetPure // Preserve purity flag
- );
+ // Use factory to create new node with TCO status
+ Result := TAst.FunctionCall(Node.Identity, newCallee, newArgsList, C.StaticType, isTailCall, C.StaticTarget, C.IsTargetPure);
end;
end.
diff --git a/Src/AST/Myc.Ast.Compiler.TypeChecker.pas b/Src/AST/Myc.Ast.Compiler.TypeChecker.pas
index bba3856..9bc2d0d 100644
--- a/Src/AST/Myc.Ast.Compiler.TypeChecker.pas
+++ b/Src/AST/Myc.Ast.Compiler.TypeChecker.pas
@@ -52,28 +52,31 @@ type
function PrepareBaseType(const BaseNode: IAstNode; out IsOptional: Boolean): IStaticType;
function ApplyOptionality(const AType: IStaticType; IsOptional: Boolean): IStaticType;
- protected
- function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
- function VisitAssignment(const Node: IAssignmentNode): IAstNode; override;
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
- function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
- function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
- function VisitMemberAccess(const Node: IMemberAccessNode): IAstNode; override;
- function VisitIndexer(const Node: IIndexerNode): IAstNode; override;
- function VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode; override;
- function VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; override;
- function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
- function VisitNop(const Node: INopNode): IAstNode; override;
- function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
-
- function VisitConstant(const Node: IConstantNode): IAstNode; override;
- function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
+ strict private
+ // Typed Handlers (non-virtual, IAstNode signature)
+ function VisitIdentifier(const Node: IAstNode): IAstNode;
+ function VisitVariableDeclaration(const Node: IAstNode): IAstNode;
+ function VisitAssignment(const Node: IAstNode): IAstNode;
+ function VisitLambdaExpression(const Node: IAstNode): IAstNode;
+ function VisitFunctionCall(const Node: IAstNode): IAstNode;
+ function VisitBlockExpression(const Node: IAstNode): IAstNode;
+ function VisitIfExpression(const Node: IAstNode): IAstNode;
+ function VisitMemberAccess(const Node: IAstNode): IAstNode;
+ function VisitIndexer(const Node: IAstNode): IAstNode;
+ function VisitCreateSeries(const Node: IAstNode): IAstNode;
+ function VisitSeriesLength(const Node: IAstNode): IAstNode;
+ function VisitRecurNode(const Node: IAstNode): IAstNode;
+ function VisitNop(const Node: IAstNode): IAstNode;
+ function VisitRecordLiteral(const Node: IAstNode): IAstNode;
+ function VisitConstant(const Node: IAstNode): IAstNode;
+ function VisitKeyword(const Node: IAstNode): IAstNode;
// Pipe Support
- function VisitPipeInput(const Node: IPipeInputNode): IAstNode; override;
- function VisitPipe(const Node: IPipeNode): IAstNode; override;
+ function VisitPipeInput(const Node: IAstNode): IAstNode;
+ function VisitPipe(const Node: IAstNode): IAstNode;
+
+ protected
+ procedure SetupHandlers; override;
public
constructor Create(const RootLayout: IScopeLayout; const RootScope: IExecutionScope; const ALog: ICompilerLog);
@@ -215,6 +218,32 @@ begin
inherited;
end;
+procedure TTypeChecker.SetupHandlers;
+begin
+ inherited SetupHandlers; // Load Defaults
+
+ Register(akIdentifier, VisitIdentifier);
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+ Register(akAssignment, VisitAssignment);
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akFunctionCall, VisitFunctionCall);
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akIfExpression, VisitIfExpression);
+ Register(akMemberAccess, VisitMemberAccess);
+ Register(akIndexer, VisitIndexer);
+ Register(akCreateSeries, VisitCreateSeries);
+ Register(akSeriesLength, VisitSeriesLength);
+ Register(akRecur, VisitRecurNode);
+ Register(akNop, VisitNop);
+ Register(akRecordLiteral, VisitRecordLiteral);
+ Register(akConstant, VisitConstant);
+ Register(akKeyword, VisitKeyword);
+
+ // Pipe Support
+ Register(akPipeInput, VisitPipeInput);
+ Register(akPipe, VisitPipe);
+end;
+
class function TTypeChecker.CheckTypes(
const RootNode: IAstNode;
const Layout: IScopeLayout;
@@ -261,24 +290,27 @@ end;
// --- Visits ---
-function TTypeChecker.VisitConstant(const Node: IConstantNode): IAstNode;
+function TTypeChecker.VisitConstant(const Node: IAstNode): IAstNode;
+begin
+ // Base implementation already returns Node, but here we explicitly confirm identity for clarity
+ Result := Node;
+end;
+
+function TTypeChecker.VisitKeyword(const Node: IAstNode): IAstNode;
begin
Result := Node;
end;
-function TTypeChecker.VisitKeyword(const Node: IKeywordNode): IAstNode;
-begin
- Result := Node;
-end;
-
-function TTypeChecker.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
+function TTypeChecker.VisitIdentifier(const Node: IAstNode): IAstNode;
var
+ I: IIdentifierNode;
typ: IStaticType;
adr: TResolvedAddress;
identity: INamedIdentity;
begin
- adr := Node.Address;
- identity := Node.Identity.AsNamed;
+ I := Node.AsIdentifier;
+ adr := I.Address;
+ identity := I.Identity.AsNamed;
if adr.Kind = akUnresolved then
begin
@@ -290,40 +322,45 @@ begin
Result := TAst.Identifier(identity, adr, typ);
end;
-function TTypeChecker.VisitRecurNode(const Node: IRecurNode): IAstNode;
+function TTypeChecker.VisitRecurNode(const Node: IAstNode): IAstNode;
var
- newArgs: TArray;
- i: Integer;
+ R: IRecurNode;
+ args: IArgumentList;
begin
- SetLength(newArgs, Node.Arguments.Count);
- for i := 0 to Node.Arguments.Count - 1 do
- newArgs[i] := Accept(Node.Arguments[i]);
+ R := Node.AsRecur;
+ // Use inherited to transform arguments, then reconstruct with type Void
+ // Note: inherited VisitRecurNode returns IAstNode which is a RecurNode.
+ // We can call Accept on arguments list directly to avoid intermediate node creation if desired,
+ // but relying on inherited logic keeps it consistent.
- var argList := TArgumentList.Create(newArgs, Node.Arguments.Identity);
- Result := TAst.Recur(Node.Identity, argList, TTypes.Void);
+ // Efficient approach: Accept the arguments list directly (it's a child).
+ args := Accept(R.Arguments).AsArgumentList;
+ Result := TAst.Recur(Node.Identity, args, TTypes.Void);
end;
-function TTypeChecker.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
+function TTypeChecker.VisitVariableDeclaration(const Node: IAstNode): IAstNode;
var
+ V: IVariableDeclarationNode;
initType: IStaticType;
newInitializer, newIdent: IAstNode;
adr: TResolvedAddress;
identNode: IIdentifierNode;
begin
- identNode := Node.Target.AsIdentifier;
+ V := Node.AsVariableDeclaration;
+ identNode := V.Target.AsIdentifier;
adr := identNode.Address;
initType := TTypes.Unknown;
if adr.Kind = akUnresolved then
begin
- if Assigned(Node.Initializer) then
- Accept(Node.Initializer);
+ if Assigned(V.Initializer) then
+ Accept(V.Initializer);
Result := Node;
Exit;
end;
- if Assigned(Node.Initializer) then
- newInitializer := Accept(Node.Initializer)
+ if Assigned(V.Initializer) then
+ newInitializer := Accept(V.Initializer)
else
newInitializer := nil;
@@ -334,29 +371,31 @@ begin
FCurrentContext.SetType(adr.SlotIndex, initType);
newIdent := TAst.Identifier(identNode.Identity.AsNamed, adr, initType);
- Result := TAst.VarDecl(Node.Identity, newIdent, newInitializer, initType, Node.IsBoxed);
+ Result := TAst.VarDecl(Node.Identity, newIdent, newInitializer, initType, V.IsBoxed);
end;
-function TTypeChecker.VisitAssignment(const Node: IAssignmentNode): IAstNode;
+function TTypeChecker.VisitAssignment(const Node: IAstNode): IAstNode;
var
+ A: IAssignmentNode;
targetType, sourceType: IStaticType;
newIdent, newValue: IAstNode;
adr: TResolvedAddress;
identNode: IIdentifierNode;
begin
- identNode := Node.Target.AsIdentifier;
- newIdent := Accept(Node.Target);
+ A := Node.AsAssignment;
+ identNode := A.Target.AsIdentifier;
+ newIdent := Accept(A.Target);
targetType := newIdent.AsTypedNode.StaticType;
adr := identNode.Address;
if adr.Kind = akUnresolved then
begin
- Accept(Node.Value);
+ Accept(A.Value);
Result := Node;
Exit;
end;
- newValue := Accept(Node.Value);
+ newValue := Accept(A.Value);
sourceType := newValue.AsTypedNode.StaticType;
if (targetType.Kind <> stUnknown) and (sourceType.Kind <> stUnknown) then
@@ -378,27 +417,28 @@ begin
Result := TAst.Assign(Node.Identity, newIdent, newValue, targetType);
end;
-function TTypeChecker.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
+function TTypeChecker.VisitBlockExpression(const Node: IAstNode): IAstNode;
var
+ newBlock: IBlockExpressionNode;
blockType: IStaticType;
- newExprs: TArray;
- i: Integer;
+ exprs: IExpressionList;
begin
- SetLength(newExprs, Node.Expressions.Count);
- for i := 0 to Node.Expressions.Count - 1 do
- newExprs[i] := Accept(Node.Expressions[i]);
+ // Inherited logic transforms all expressions in the list
+ newBlock := inherited VisitBlockExpression(Node).AsBlockExpression;
+ exprs := newBlock.Expressions;
- if Length(newExprs) > 0 then
- blockType := newExprs[High(newExprs)].AsTypedNode.StaticType
+ if exprs.Count > 0 then
+ blockType := exprs[exprs.Count - 1].AsTypedNode.StaticType
else
blockType := TTypes.Void;
- var exprList := TExpressionList.Create(newExprs, Node.Expressions.Identity);
- Result := TAst.Block(Node.Identity, exprList, blockType);
+ // Return new block with calculated type
+ Result := TAst.Block(Node.Identity, exprs, blockType);
end;
-function TTypeChecker.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
+function TTypeChecker.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
+ L: ILambdaExpressionNode;
newParams: TArray;
newBody: IAstNode;
bodyType, methodType: IStaticType;
@@ -409,8 +449,10 @@ var
paramIdent: IIdentifierNode;
injectedType: IStaticType;
begin
+ L := Node.AsLambdaExpression;
+
// 1. Resolve Upvalue Types
- var upvalueAddrs := Node.Upvalues;
+ var upvalueAddrs := L.Upvalues;
SetLength(upvalueTypes, Length(upvalueAddrs));
for i := 0 to High(upvalueAddrs) do
@@ -425,14 +467,14 @@ begin
end;
// 2. Enter New Scope
- FCurrentContext := TTypeContext.Create(FCurrentContext, Node.Layout, upvalueTypes, nil);
+ FCurrentContext := TTypeContext.Create(FCurrentContext, L.Layout, upvalueTypes, nil);
try
- SetLength(newParams, Node.Parameters.Count);
- SetLength(paramTypes, Node.Parameters.Count);
+ SetLength(newParams, L.Parameters.Count);
+ SetLength(paramTypes, L.Parameters.Count);
- for i := 0 to Node.Parameters.Count - 1 do
+ for i := 0 to L.Parameters.Count - 1 do
begin
- paramIdent := Node.Parameters[i];
+ paramIdent := L.Parameters[i];
// Check if there is already a type assigned (e.g. injected by Pipe Visitor)
injectedType := paramIdent.AsTypedNode.StaticType;
@@ -448,52 +490,45 @@ begin
newParams[i] := TAst.Identifier(paramIdent.Identity.AsNamed, paramIdent.Address, paramTypes[i]);
end;
- newBody := Accept(Node.Body);
+ newBody := Accept(L.Body);
bodyType := newBody.AsTypedNode.StaticType;
methodType := TTypes.CreateMethod(paramTypes, bodyType);
- finalDescriptor := TScope.CreateDescriptor(Node.Layout, FCurrentContext.Types);
+ finalDescriptor := TScope.CreateDescriptor(L.Layout, FCurrentContext.Types);
finally
var temp := FCurrentContext;
FCurrentContext := FCurrentContext.FParent;
temp.Free;
end;
- var paramList := TParameterList.Create(newParams, Node.Parameters.Identity);
+ var paramList := TParameterList.Create(newParams, L.Parameters.Identity);
Result :=
- TAst.LambdaExpr(
- Node.Identity,
- paramList,
- newBody,
- Node.Layout,
- finalDescriptor,
- Node.Upvalues,
- Node.HasNestedLambdas,
- Node.IsPure,
- methodType
- );
+ TAst.LambdaExpr(Node.Identity, paramList, newBody, L.Layout, finalDescriptor, L.Upvalues, L.HasNestedLambdas, L.IsPure, methodType);
end;
-function TTypeChecker.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
+function TTypeChecker.VisitFunctionCall(const Node: IAstNode): IAstNode;
var
+ newCall: IFunctionCallNode;
calleeType, retType: IStaticType;
i, j: Integer;
- newCallee: IAstNode;
- newArgs: TArray;
argTypes: TArray;
hasUnknownArgs: Boolean;
bestSig: IMethodSignature;
match: Boolean;
begin
- newCallee := Accept(Node.Callee);
- SetLength(newArgs, Node.Arguments.Count);
- SetLength(argTypes, Node.Arguments.Count);
+ // Use inherited to visit Callee and Arguments first
+ newCall := inherited VisitFunctionCall(Node).AsFunctionCall;
+
+ // Now analyze types on the transformed children
+ var newCallee := newCall.Callee;
+ var newArgs := newCall.Arguments;
+
+ SetLength(argTypes, newArgs.Count);
hasUnknownArgs := False;
- for i := 0 to Node.Arguments.Count - 1 do
+ for i := 0 to newArgs.Count - 1 do
begin
- newArgs[i] := Accept(Node.Arguments[i]);
argTypes[i] := newArgs[i].AsTypedNode.StaticType;
if argTypes[i].Kind = stUnknown then
hasUnknownArgs := True;
@@ -535,36 +570,35 @@ begin
if Assigned(FLog) then
FLog.AddError(Format('Cannot invoke type %s as a function.', [calleeType.ToString]), Node);
- var argList := TArgumentList.Create(newArgs, Node.Arguments.Identity);
- Result := TAst.FunctionCall(Node.Identity, newCallee, argList, retType, Node.IsTailCall, nil, False);
+ // Return new node with Types
+ Result := TAst.FunctionCall(Node.Identity, newCallee, newArgs, retType, newCall.IsTailCall, nil, False);
end;
-function TTypeChecker.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
+function TTypeChecker.VisitIfExpression(const Node: IAstNode): IAstNode;
var
- newCond, newThen, newElse: IAstNode;
+ newIf: IIfExpressionNode;
resType: IStaticType;
begin
- newCond := Accept(Node.Condition);
- newThen := Accept(Node.ThenBranch);
- newElse := Accept(Node.ElseBranch);
+ newIf := inherited VisitIfExpression(Node).AsIfExpression;
- // Simple promotion logic
- if (newElse <> nil) then
- resType := TTypeRules.Promote(newThen.AsTypedNode.StaticType, newElse.AsTypedNode.StaticType)
+ if (newIf.ElseBranch <> nil) then
+ resType := TTypeRules.Promote(newIf.ThenBranch.AsTypedNode.StaticType, newIf.ElseBranch.AsTypedNode.StaticType)
else
- resType := TTypes.MakeOptional(newThen.AsTypedNode.StaticType);
+ resType := TTypes.MakeOptional(newIf.ThenBranch.AsTypedNode.StaticType);
- Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, resType);
+ Result := TAst.IfExpr(Node.Identity, newIf.Condition, newIf.ThenBranch, newIf.ElseBranch, resType);
end;
-function TTypeChecker.VisitIndexer(const Node: IIndexerNode): IAstNode;
+function TTypeChecker.VisitIndexer(const Node: IAstNode): IAstNode;
var
+ I: IIndexerNode;
newBase, newIndex: IAstNode;
baseType, elemType: IStaticType;
isOpt: Boolean;
begin
- newBase := Accept(Node.Base);
- newIndex := Accept(Node.Index);
+ I := Node.AsIndexer;
+ newBase := Accept(I.Base);
+ newIndex := Accept(I.Index);
baseType := PrepareBaseType(newBase, isOpt);
elemType := TTypes.Unknown;
@@ -577,20 +611,22 @@ begin
Result := TAst.Indexer(Node.Identity, newBase, newIndex, elemType);
end;
-function TTypeChecker.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
+function TTypeChecker.VisitMemberAccess(const Node: IAstNode): IAstNode;
var
+ M: IMemberAccessNode;
newBase: IAstNode;
baseType, resType: IStaticType;
idx: Integer;
isOpt: Boolean;
begin
- newBase := Accept(Node.Base);
+ M := Node.AsMemberAccess;
+ newBase := Accept(M.Base);
baseType := PrepareBaseType(newBase, isOpt);
resType := TTypes.Unknown;
if (baseType.Kind = stRecord) or (baseType.Kind = stRecordSeries) then
begin
- idx := baseType.Definition.IndexOf(Node.Member.Value);
+ idx := baseType.Definition.IndexOf(M.Member.Value);
if idx >= 0 then
begin
var fieldType := TTypes.FromScalarKind(baseType.Definition.Items[idx].Value);
@@ -604,45 +640,40 @@ begin
end;
resType := ApplyOptionality(resType, isOpt);
- Result := TAst.MemberAccess(Node.Identity, newBase, Node.Member, resType);
+ Result := TAst.MemberAccess(Node.Identity, newBase, M.Member, resType);
end;
-function TTypeChecker.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
+function TTypeChecker.VisitRecordLiteral(const Node: IAstNode): IAstNode;
var
+ R: IRecordLiteralNode;
i: Integer;
- newFields: TArray;
fieldTypes: TArray>;
scalarFieldTypes: TArray>;
isScalar: Boolean;
valType: IStaticType;
key: IKeyword;
- visitedValue: IAstNode;
newFieldList: IRecordFieldList;
begin
- SetLength(newFields, Node.Fields.Count);
- SetLength(fieldTypes, Node.Fields.Count);
- SetLength(scalarFieldTypes, Node.Fields.Count);
+ R := Node.AsRecordLiteral;
+ // Transform fields using inherited recursion
+ newFieldList := inherited VisitRecordFieldList(R.Fields).AsRecordFieldList;
+
+ // Now analyze the transformed fields
+ var count := newFieldList.Count;
+ SetLength(fieldTypes, count);
+ SetLength(scalarFieldTypes, count);
isScalar := True;
- for i := 0 to Node.Fields.Count - 1 do
+ for i := 0 to count - 1 do
begin
- var oldField := Node.Fields[i];
- visitedValue := Accept(oldField.Value);
-
- // Keys are guaranteed to be keywords by parser
- key := oldField.Key.Value;
-
- // Recreate the field node with the typed value
- newFields[i] := TAst.RecordField(oldField.Identity, oldField.Key, visitedValue);
-
- // Analyze Type
- valType := visitedValue.AsTypedNode.StaticType;
+ var field := newFieldList[i];
+ key := field.Key.Value;
+ valType := field.Value.AsTypedNode.StaticType;
// Check if strict scalar (no optionals allowed in packed ScalarRecord)
if (valType.Kind in [stOrdinal, stFloat, stBoolean, stDateTime, stKeyword]) and (not valType.IsOptional) then
begin
var kind: TScalar.TKind;
- // Map StaticType Kind to Scalar Kind
case valType.Kind of
stOrdinal: kind := TScalar.TKind.Ordinal;
stFloat: kind := TScalar.TKind.Float;
@@ -650,7 +681,7 @@ begin
stDateTime: kind := TScalar.TKind.DateTime;
stKeyword: kind := TScalar.TKind.Keyword;
else
- kind := TScalar.TKind.Ordinal; // Should not happen given check above
+ kind := TScalar.TKind.Ordinal;
end;
scalarFieldTypes[i] := TPair.Create(key, kind);
end
@@ -667,7 +698,7 @@ begin
var genericDef: IGenericRecordDefinition := nil;
var resultType: IStaticType;
- if isScalar and (Length(newFields) > 0) then
+ if isScalar and (count > 0) then
begin
scalarDef := TKeywordMappingRegistry.Intern(scalarFieldTypes);
resultType := TTypes.CreateRecord(scalarDef);
@@ -678,16 +709,17 @@ begin
resultType := TTypes.CreateGenericRecord(genericDef);
end;
- newFieldList := TRecordFieldList.Create(newFields, Node.Fields.Identity);
Result := TAst.RecordLiteral(Node.Identity, newFieldList, scalarDef, genericDef, resultType);
end;
-function TTypeChecker.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
+function TTypeChecker.VisitCreateSeries(const Node: IAstNode): IAstNode;
var
+ C: ICreateSeriesNode;
elemType: IStaticType;
def: string;
begin
- def := Node.Definition;
+ C := Node.AsCreateSeries;
+ def := C.Definition;
// Simple heuristic for type
if def.StartsWith('[') then
elemType := TTypes.CreateRecord(nil) // Placeholder, normally parses JSON
@@ -697,12 +729,12 @@ begin
Result := TAst.CreateSeries(Node.Identity.AsDefinition, TTypes.CreateSeries(elemType));
end;
-function TTypeChecker.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode;
+function TTypeChecker.VisitSeriesLength(const Node: IAstNode): IAstNode;
begin
- Result := TAst.SeriesLength(Node.Identity, Accept(Node.Series).AsIdentifier, TTypes.Ordinal);
+ Result := TAst.SeriesLength(Node.Identity, Accept(Node.AsSeriesLength.Series).AsIdentifier, TTypes.Ordinal);
end;
-function TTypeChecker.VisitNop(const Node: INopNode): IAstNode;
+function TTypeChecker.VisitNop(const Node: IAstNode): IAstNode;
begin
Result := TAst.Nop(Node.Identity, TTypes.Void);
end;
@@ -711,14 +743,15 @@ end;
// PIPE IMPLEMENTATION (Type Checking)
// =============================================================================
-function TTypeChecker.VisitPipeInput(const Node: IPipeInputNode): IAstNode;
+function TTypeChecker.VisitPipeInput(const Node: IAstNode): IAstNode;
var
+ P: IPipeInputNode;
newSource: IIdentifierNode;
sourceType: IStaticType;
- newSelectors: TArray;
- i: Integer;
begin
- newSource := Accept(Node.StreamSource).AsIdentifier;
+ P := Node.AsPipeInput;
+ // Transform source identifier (resolves type)
+ newSource := Accept(P.StreamSource).AsIdentifier;
sourceType := newSource.AsTypedNode.StaticType;
// 1. Verify Source is a Series-compatible type
@@ -736,7 +769,7 @@ begin
begin
// 2. Verify Selectors exist in Record Definition
var def := sourceType.Definition;
- for var sel in Node.Selectors do
+ for var sel in P.Selectors do
begin
if def.IndexOf(sel.Value) < 0 then
begin
@@ -747,16 +780,13 @@ begin
end;
end;
- // Reconstruct list simply to pass through
- SetLength(newSelectors, Node.Selectors.Count);
- for i := 0 to Node.Selectors.Count - 1 do
- newSelectors[i] := Node.Selectors[i];
-
- Result := TAst.PipeInput(newSource, TAst.PipeSelectorList(newSelectors, Node.Selectors.Identity.Location), Node.Identity.Location);
+ // Reuse selectors (they are just keywords, no type checking needed)
+ Result := TAst.PipeInput(newSource, P.Selectors, Node.Identity.Location);
end;
-function TTypeChecker.VisitPipe(const Node: IPipeNode): IAstNode;
+function TTypeChecker.VisitPipe(const Node: IAstNode): IAstNode;
var
+ P: IPipeNode;
i, k: Integer;
inputNode: IPipeInputNode;
newInputs: TArray;
@@ -766,13 +796,15 @@ var
newParams: TArray;
inferredType: IStaticType;
begin
- SetLength(newInputs, Node.Inputs.Count);
+ P := Node.AsPipe;
+ SetLength(newInputs, P.Inputs.Count);
paramTypes := TList.Create;
try
// 1. Visit Inputs and collect types for Lambda parameters
- for i := 0 to Node.Inputs.Count - 1 do
+ for i := 0 to P.Inputs.Count - 1 do
begin
- inputNode := Accept(Node.Inputs[i]).AsPipeInput;
+ // Recurse on inputs to resolve their sources
+ inputNode := Accept(P.Inputs[i]).AsPipeInput;
newInputs[i] := inputNode;
streamType := inputNode.StreamSource.AsTypedNode.StaticType;
@@ -803,7 +835,7 @@ begin
end;
// 2. Prepare Lambda with Inferred Types
- lambda := Node.Transformation;
+ lambda := P.Transformation;
if lambda.Parameters.Count <> paramTypes.Count then
begin
@@ -871,7 +903,7 @@ begin
pipeType := TTypes.Unknown;
end;
- Result := TAst.Pipe(Node.Identity, TPipeInputList.Create(newInputs, Node.Inputs.Identity), typedLambda, pipeType);
+ Result := TAst.Pipe(Node.Identity, TPipeInputList.Create(newInputs, P.Inputs.Identity), typedLambda, pipeType);
finally
paramTypes.Free;
end;
diff --git a/Src/AST/Myc.Ast.Debugger.pas b/Src/AST/Myc.Ast.Debugger.pas
index 70c1824..22c5d57 100644
--- a/Src/AST/Myc.Ast.Debugger.pas
+++ b/Src/AST/Myc.Ast.Debugger.pas
@@ -11,6 +11,7 @@ uses
Myc.Ast.Nodes,
Myc.Ast.Scope,
Myc.Ast,
+ Myc.Ast.Visitor,
Myc.Ast.Evaluator;
type
@@ -19,41 +20,25 @@ type
FLog: TStrings;
FIndentLevel: Integer;
FShowScope: Boolean;
+
procedure Indent;
procedure Unindent;
procedure AppendLine(const S: string);
procedure ShowScope;
-
+ function GetNodeLogInfo(const Node: IAstNode): string;
protected
- // Overridden to provide the debug-specific visitor factory for nested calls (Lambdas).
function CreateVisitorFactory: TEvaluatorFactory; override;
+ // Central Interceptor for all node types
+ function Visit(const Node: IAstNode): TDataValue; override;
+
+ // Specific overrides for enhanced logging/state observation
+ function VisitVariableDeclaration(const N: IVariableDeclarationNode): TDataValue; override;
+ function VisitAssignment(const N: IAssignmentNode): TDataValue; override;
+ function VisitFunctionCall(const N: IFunctionCallNode): TDataValue; override;
+ function VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue; override;
public
constructor Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0);
-
- // The logging overrides for Visit... methods
- function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
- function VisitConstant(const Node: IConstantNode): TDataValue; override;
- function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
- function VisitKeyword(const Node: IKeywordNode): TDataValue; override;
- function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
- function VisitCondExpression(const Node: ICondExpressionNode): TDataValue; override; // Replaces Ternary
- function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override;
- function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
- function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
- function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
- function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override;
- function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
- function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
- function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
- function VisitNop(const Node: INopNode): TDataValue; override;
-
- // Pipe Support
- function VisitPipeInput(const Node: IPipeInputNode): TDataValue; override;
- function VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; override;
- function VisitPipeInputList(const Node: IPipeInputList): TDataValue; override;
- function VisitPipe(const Node: IPipeNode): TDataValue; override;
end;
implementation
@@ -64,26 +49,21 @@ uses
{ TDebugEvaluatorVisitor }
-constructor TDebugEvaluatorVisitor.Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0);
+constructor TDebugEvaluatorVisitor.Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer);
begin
inherited Create(AScope);
- Assert(Assigned(ALog));
FLog := ALog;
FIndentLevel := AInitialIndent;
FShowScope := AShowScope;
- ShowScope;
+ if FShowScope and (AInitialIndent = 0) then
+ ShowScope;
end;
function TDebugEvaluatorVisitor.CreateVisitorFactory: TEvaluatorFactory;
begin
- // Return a closure that creates a new Debug visitor, ensuring the debug context (Log, Indent) is passed down.
- // This is used by TEvaluatorVisitor.VisitLambdaExpression when executing the closure body.
-
- // Capture current state
var currentLog := FLog;
var currentShowScope := FShowScope;
- var currentIndent := FIndentLevel; // Pass current indent as base for new frame
-
+ var currentIndent := FIndentLevel;
Result :=
function(const AScope: IExecutionScope): IEvaluatorVisitor
begin
@@ -91,14 +71,98 @@ begin
end;
end;
-procedure TDebugEvaluatorVisitor.Indent;
+function TDebugEvaluatorVisitor.Visit(const Node: IAstNode): TDataValue;
+var
+ info: string;
begin
- inc(FIndentLevel);
+ info := GetNodeLogInfo(Node);
+ if info <> '' then
+ begin
+ AppendLine(info + ' {');
+ Indent;
+ end;
+
+ try
+ // Dispatch to inherited logic (which will call our specialized VisitXyz overrides)
+ Result := inherited Visit(Node);
+ finally
+ if info <> '' then
+ begin
+ Unindent;
+ var resStr :=
+ if Result.IsVoid then '(void)'
+ else Result.ToString;
+ AppendLine('} -> ' + resStr);
+ end;
+ end;
end;
+// --- Enhanced Observation Overrides ---
+
+function TDebugEvaluatorVisitor.VisitVariableDeclaration(const N: IVariableDeclarationNode): TDataValue;
+begin
+ Result := inherited VisitVariableDeclaration(N);
+ if FShowScope then
+ ShowScope;
+end;
+
+function TDebugEvaluatorVisitor.VisitAssignment(const N: IAssignmentNode): TDataValue;
+begin
+ Result := inherited VisitAssignment(N);
+ if FShowScope then
+ ShowScope;
+end;
+
+function TDebugEvaluatorVisitor.VisitFunctionCall(const N: IFunctionCallNode): TDataValue;
+begin
+ // Log target purity if statically known
+ if Assigned(N.StaticTarget) and N.IsTargetPure then
+ AppendLine('[Pure Static Call]');
+ Result := inherited VisitFunctionCall(N);
+end;
+
+function TDebugEvaluatorVisitor.VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue;
+begin
+ if Length(N.Upvalues) > 0 then
+ AppendLine(Format('[Capturing %d upvalues]', [Length(N.Upvalues)]));
+ Result := inherited VisitLambdaExpression(N);
+end;
+
+// --- Internal Helpers ---
+
+function TDebugEvaluatorVisitor.GetNodeLogInfo(const Node: IAstNode): string;
+begin
+ case Node.Kind of
+ akFunctionCall:
+ begin
+ var c := Node.AsFunctionCall;
+ var mode :=
+ if Assigned(c.StaticTarget) then 'STATIC'
+ else 'DYNAMIC';
+ Result := Format('Call (%s, Tail=%s)', [mode, c.IsTailCall.ToString(TUseBoolStrs.True)]);
+ end;
+ akIdentifier: Result := 'ID:' + Node.AsIdentifier.Name;
+ akVariableDeclaration: Result := 'DEF:' + Node.AsVariableDeclaration.Target.AsIdentifier.Name;
+ akAssignment: Result := 'ASSIGN:' + Node.AsAssignment.Target.AsIdentifier.Name;
+ akIfExpression: Result := 'IF';
+ akCondExpression: Result := 'COND';
+ akRecur: Result := 'RECUR';
+ akBlockExpression: Result := 'BLOCK';
+ akLambdaExpression: Result := 'FN';
+ akRecordLiteral: Result := 'RECORD';
+ akPipe: Result := 'PIPE';
+ else
+ Result := ''; // Silence leaf nodes like Constants and Keywords to reduce noise
+ end;
+end;
+
+procedure TDebugEvaluatorVisitor.Indent;
+begin
+ Inc(FIndentLevel);
+end;
procedure TDebugEvaluatorVisitor.Unindent;
begin
- dec(FIndentLevel);
+ Dec(FIndentLevel);
end;
procedure TDebugEvaluatorVisitor.AppendLine(const S: string);
@@ -114,255 +178,11 @@ end;
procedure TDebugEvaluatorVisitor.ShowScope;
var
- scopeDump: TArray;
line: string;
begin
- if FShowScope then
- begin
- AppendLine('-- Scope --');
- // Scope.Dump logic has been updated in Myc.Ast.Scope to handle new architecture
- scopeDump := Scope.Dump.Split([sLineBreak]);
- for line in scopeDump do
- begin
- AppendLine(line);
- end;
- AppendLine('-----------');
- end;
-end;
-
-// --- Visit Overrides ---
-
-function TDebugEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
-var
- mode: string;
-begin
- if Assigned(Node.StaticTarget) then
- mode := 'STATIC'
- else
- mode := 'DYNAMIC';
-
- AppendLine(Format('FunctionCall (%s, Tail=%s) {', [mode, Node.IsTailCall.ToString(TUseBoolStrs.True)]));
- Indent;
- try
- // ShowScope is called in Constructor of new Visitor (via CreateVisitorFactory) for the body,
- // but for arguments evaluation here in the current scope, we might want to see it.
- ShowScope;
- Result := inherited VisitFunctionCall(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
-begin
- AppendLine('AddSeriesItem {');
- Indent;
- try
- Result := inherited VisitAddSeriesItem(Node);
- finally
- Unindent;
- end;
- AppendLine('} -> (void)');
-end;
-
-function TDebugEvaluatorVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue;
-begin
- AppendLine(Format('Assignment to "%s" {', [Node.Target.AsIdentifier.Name]));
- Indent;
- try
- Result := inherited VisitAssignment(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
-begin
- Result := inherited VisitConstant(Node);
- AppendLine(Format('Constant (%s)', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue;
-begin
- Result := inherited VisitKeyword(Node);
- AppendLine(Format('Keyword (%s)', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
-begin
- AppendLine('CreateSeries {');
- Indent;
- try
- Result := inherited VisitCreateSeries(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
-begin
- Result := inherited VisitIdentifier(Node);
- AppendLine(Format('Identifier "%s" -> %s', [Node.Name, Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
-begin
- AppendLine('IfExpr {');
- Indent;
- try
- Result := inherited VisitIfExpression(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
-begin
- AppendLine('Indexer {');
- Indent;
- try
- Result := inherited VisitIndexer(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
-begin
- AppendLine(Format('MemberAccess (Member: %s) {', [Node.Member.Value.Name]));
- Indent;
- try
- Result := inherited VisitMemberAccess(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
-begin
- AppendLine('RecordLiteral {');
- Indent;
- try
- Result := inherited VisitRecordLiteral(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitCondExpression(const Node: ICondExpressionNode): TDataValue;
-begin
- AppendLine(Format('CondExpr (%d pairs) {', [Length(Node.Pairs)]));
- Indent;
- try
- // We just call inherited, which does the logic.
- // We don't log every branch check here to avoid spamming output,
- // but the recursive evaluation of conditions will show up in the log naturally.
- Result := inherited VisitCondExpression(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
-begin
- AppendLine('Block {');
- Indent;
- try
- // Delegates to VisitExpressionList via inherited
- Result := inherited VisitBlockExpression(Node);
- // Scope might have changed after block execution if vars were defined
- ShowScope;
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
-begin
- AppendLine(Format('VarDecl %s :=', [Node.Target.AsIdentifier.Name]));
- Indent;
- try
- Result := inherited VisitVariableDeclaration(Node);
- finally
- Unindent;
- end;
-end;
-
-function TDebugEvaluatorVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
-begin
- AppendLine('SeriesLength {');
- Indent;
- try
- Result := inherited VisitSeriesLength(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitNop(const Node: INopNode): TDataValue;
-begin
- AppendLine('Nop (void)');
- Result := TDataValue.Void;
-end;
-
-// --- Pipe Support ---
-
-function TDebugEvaluatorVisitor.VisitPipe(const Node: IPipeNode): TDataValue;
-begin
- AppendLine('Pipe {');
- Indent;
- try
- Result := inherited VisitPipe(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
-function TDebugEvaluatorVisitor.VisitPipeInput(const Node: IPipeInputNode): TDataValue;
-begin
- AppendLine(Format('PipeInput (Source: %s) {', [Node.StreamSource.Name]));
- Indent;
- try
- Result := inherited VisitPipeInput(Node);
- finally
- Unindent;
- end;
- AppendLine('}');
-end;
-
-function TDebugEvaluatorVisitor.VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue;
-begin
- AppendLine('Selectors [');
- Indent;
- try
- Result := inherited VisitPipeSelectorList(Node);
- finally
- Unindent;
- end;
- AppendLine(']');
-end;
-
-function TDebugEvaluatorVisitor.VisitPipeInputList(const Node: IPipeInputList): TDataValue;
-begin
- AppendLine('Inputs [');
- Indent;
- try
- Result := inherited VisitPipeInputList(Node);
- finally
- Unindent;
- end;
- AppendLine(']');
+ AppendLine(' [Scope State]');
+ for line in Scope.Dump.Split([sLineBreak]) do
+ AppendLine(' ' + line);
end;
end.
diff --git a/Src/AST/Myc.Ast.Dumper.pas b/Src/AST/Myc.Ast.Dumper.pas
index 48041a5..f78c0a6 100644
--- a/Src/AST/Myc.Ast.Dumper.pas
+++ b/Src/AST/Myc.Ast.Dumper.pas
@@ -29,38 +29,47 @@ type
procedure LogFmt(const Fmt: string; const Args: array of const; const Node: IAstNode = nil); overload;
function FormatAddress(const Addr: TResolvedAddress): string;
- protected
- function VisitConstant(const Node: IConstantNode): TVoid; override;
- function VisitIdentifier(const Node: IIdentifierNode): TVoid; override;
- function VisitKeyword(const Node: IKeywordNode): TVoid; override;
- function VisitIfExpression(const Node: IIfExpressionNode): TVoid; override;
- function VisitCondExpression(const Node: ICondExpressionNode): TVoid; override; // Replaced Ternary
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): TVoid; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): TVoid; override;
- function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TVoid; override;
- function VisitRecurNode(const Node: IRecurNode): TVoid; override;
- function VisitBlockExpression(const Node: IBlockExpressionNode): TVoid; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TVoid; override;
- function VisitAssignment(const Node: IAssignmentNode): TVoid; override;
- function VisitMacroDefinition(const Node: IMacroDefinitionNode): TVoid; override;
- function VisitQuasiquote(const Node: IQuasiquoteNode): TVoid; override;
- function VisitUnquote(const Node: IUnquoteNode): TVoid; override;
- function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TVoid; override;
- function VisitIndexer(const Node: IIndexerNode): TVoid; override;
- function VisitMemberAccess(const Node: IMemberAccessNode): TVoid; override;
- function VisitRecordLiteral(const Node: IRecordLiteralNode): TVoid; override;
- function VisitCreateSeries(const Node: ICreateSeriesNode): TVoid; override;
- function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TVoid; override;
- function VisitSeriesLength(const Node: ISeriesLengthNode): TVoid; override;
- function VisitNop(const Node: INopNode): TVoid; override;
+ strict private
+ // Internal visit helpers with strict IAstNode signature
+ function VisitConstant(const Node: IAstNode): TVoid;
+ function VisitIdentifier(const Node: IAstNode): TVoid;
+ function VisitKeyword(const Node: IAstNode): TVoid;
+ function VisitIfExpression(const Node: IAstNode): TVoid;
+ function VisitCondExpression(const Node: IAstNode): TVoid;
+ function VisitLambdaExpression(const Node: IAstNode): TVoid;
+ function VisitFunctionCall(const Node: IAstNode): TVoid;
+ function VisitMacroExpansionNode(const Node: IAstNode): TVoid;
+ function VisitRecurNode(const Node: IAstNode): TVoid;
+ function VisitBlockExpression(const Node: IAstNode): TVoid;
+ function VisitVariableDeclaration(const Node: IAstNode): TVoid;
+ function VisitAssignment(const Node: IAstNode): TVoid;
+ function VisitMacroDefinition(const Node: IAstNode): TVoid;
+ function VisitQuasiquote(const Node: IAstNode): TVoid;
+ function VisitUnquote(const Node: IAstNode): TVoid;
+ function VisitUnquoteSplicing(const Node: IAstNode): TVoid;
+ function VisitIndexer(const Node: IAstNode): TVoid;
+ function VisitMemberAccess(const Node: IAstNode): TVoid;
+ function VisitRecordLiteral(const Node: IAstNode): TVoid;
+ function VisitCreateSeries(const Node: IAstNode): TVoid;
+ function VisitAddSeriesItem(const Node: IAstNode): TVoid;
+ function VisitSeriesLength(const Node: IAstNode): TVoid;
+ function VisitNop(const Node: IAstNode): TVoid;
- // List Visitors are handled implicitly by parent node iteration in Dumper,
- // but we implement them empty/default to satisfy the abstract base class if called directly.
- function VisitParameterList(const Node: IParameterList): TVoid; override;
- function VisitArgumentList(const Node: IArgumentList): TVoid; override;
- function VisitExpressionList(const Node: IExpressionList): TVoid; override;
- function VisitRecordFieldList(const Node: IRecordFieldList): TVoid; override;
- function VisitRecordField(const Node: IRecordFieldNode): TVoid; override;
+ // List Visitors
+ function VisitParameterList(const Node: IAstNode): TVoid;
+ function VisitArgumentList(const Node: IAstNode): TVoid;
+ function VisitExpressionList(const Node: IAstNode): TVoid;
+ function VisitRecordFieldList(const Node: IAstNode): TVoid;
+ function VisitRecordField(const Node: IAstNode): TVoid;
+
+ // Pipe Visitors
+ function VisitPipeInput(const Node: IAstNode): TVoid;
+ function VisitPipeSelectorList(const Node: IAstNode): TVoid;
+ function VisitPipeInputList(const Node: IAstNode): TVoid;
+ function VisitPipe(const Node: IAstNode): TVoid;
+
+ protected
+ procedure SetupHandlers; override;
public
constructor Create(const AOutput: TStrings);
@@ -81,7 +90,9 @@ var
dumper: TAstDumper;
begin
if (not Assigned(Output)) or (not Assigned(RootNode)) then
+ begin
exit;
+ end;
Output.Clear;
dumper := TAstDumper.Create(Output);
@@ -99,10 +110,51 @@ begin
FIndent := 0;
end;
+procedure TAstDumper.SetupHandlers;
+begin
+ Register(akConstant, VisitConstant);
+ Register(akIdentifier, VisitIdentifier);
+ Register(akKeyword, VisitKeyword);
+
+ Register(akParameterList, VisitParameterList);
+ Register(akArgumentList, VisitArgumentList);
+ Register(akExpressionList, VisitExpressionList);
+ Register(akRecordFieldList, VisitRecordFieldList);
+ Register(akRecordField, VisitRecordField);
+
+ Register(akIfExpression, VisitIfExpression);
+ Register(akCondExpression, VisitCondExpression);
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akFunctionCall, VisitFunctionCall);
+ Register(akMacroExpansion, VisitMacroExpansionNode);
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+ Register(akAssignment, VisitAssignment);
+ Register(akMacroDefinition, VisitMacroDefinition);
+ Register(akQuasiquote, VisitQuasiquote);
+ Register(akUnquote, VisitUnquote);
+ Register(akUnquoteSplicing, VisitUnquoteSplicing);
+ Register(akIndexer, VisitIndexer);
+ Register(akMemberAccess, VisitMemberAccess);
+ Register(akRecordLiteral, VisitRecordLiteral);
+ Register(akCreateSeries, VisitCreateSeries);
+ Register(akAddSeriesItem, VisitAddSeriesItem);
+ Register(akSeriesLength, VisitSeriesLength);
+ Register(akRecur, VisitRecurNode);
+ Register(akNop, VisitNop);
+
+ Register(akPipeInput, VisitPipeInput);
+ Register(akPipeSelectorList, VisitPipeSelectorList);
+ Register(akPipeInputList, VisitPipeInputList);
+ Register(akPipe, VisitPipe);
+end;
+
procedure TAstDumper.Execute(const RootNode: IAstNode);
begin
if Assigned(RootNode) then
- RootNode.Accept(Self);
+ begin
+ Visit(RootNode);
+ end;
end;
procedure TAstDumper.Indent;
@@ -115,26 +167,28 @@ begin
dec(FIndent, 2);
end;
-procedure TAstDumper.Log(const Text: string; const Node: IAstNode = nil);
+procedure TAstDumper.Log(const Text: string; const Node: IAstNode);
var
typeStr: string;
- typedNode: IAstTypedNode;
staticType: IStaticType;
begin
typeStr := '';
if Assigned(Node) and Node.IsTyped then
begin
- typedNode := Node.AsTypedNode;
- staticType := typedNode.StaticType;
+ staticType := Node.AsTypedNode.StaticType;
if Assigned(staticType) then
- typeStr := Format(' ', [staticType.ToString])
+ begin
+ typeStr := Format(' ', [staticType.ToString]);
+ end
else
+ begin
typeStr := ' ';
+ end;
end;
FOutput.Add(StringOfChar(' ', FIndent) + Text + typeStr);
end;
-procedure TAstDumper.LogFmt(const Fmt: string; const Args: array of const; const Node: IAstNode = nil);
+procedure TAstDumper.LogFmt(const Fmt: string; const Args: array of const; const Node: IAstNode);
begin
Log(Format(Fmt, Args), Node);
end;
@@ -150,402 +204,424 @@ begin
end;
end;
-function TAstDumper.VisitConstant(const Node: IConstantNode): TVoid;
+function TAstDumper.VisitConstant(const Node: IAstNode): TVoid;
begin
- LogFmt('Constant: %s', [Node.Value.ToString], Node);
+ LogFmt('Constant: %s', [Node.AsConstant.Value.ToString], Node);
end;
-function TAstDumper.VisitIdentifier(const Node: IIdentifierNode): TVoid;
+function TAstDumper.VisitIdentifier(const Node: IAstNode): TVoid;
var
- adr: TResolvedAddress;
+ I: IIdentifierNode;
begin
- adr := Node.Address;
- if adr.Kind <> akUnresolved then
- LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(adr)], Node)
+ I := Node.AsIdentifier;
+ if I.Address.Kind <> akUnresolved then
+ begin
+ LogFmt('Identifier: %s -> %s', [I.Name, FormatAddress(I.Address)], Node);
+ end
else
- LogFmt('Identifier: %s (unbound)', [Node.Name], Node);
+ begin
+ LogFmt('Identifier: %s (unbound)', [I.Name], Node);
+ end;
end;
-function TAstDumper.VisitKeyword(const Node: IKeywordNode): TVoid;
+function TAstDumper.VisitKeyword(const Node: IAstNode): TVoid;
begin
- LogFmt('Keyword: :%s', [Node.Value.Name], Node);
+ LogFmt('Keyword: :%s', [Node.AsKeyword.Value.Name], Node);
end;
-function TAstDumper.VisitIfExpression(const Node: IIfExpressionNode): TVoid;
+function TAstDumper.VisitIfExpression(const Node: IAstNode): TVoid;
+var
+ E: IIfExpressionNode;
begin
+ E := Node.AsIfExpression;
Log('IfExpression', Node);
Indent;
Log('Condition:');
- Node.Condition.Accept(Self);
+ Visit(E.Condition);
Log('Then:');
- Node.ThenBranch.Accept(Self);
- if Assigned(Node.ElseBranch) then
+ Visit(E.ThenBranch);
+ if Assigned(E.ElseBranch) then
begin
Log('Else:');
- Node.ElseBranch.Accept(Self);
+ Visit(E.ElseBranch);
end;
Unindent;
end;
-function TAstDumper.VisitCondExpression(const Node: ICondExpressionNode): TVoid;
+function TAstDumper.VisitCondExpression(const Node: IAstNode): TVoid;
var
+ E: ICondExpressionNode;
i: Integer;
begin
- LogFmt('CondExpression (%d pairs)', [Length(Node.Pairs)], Node);
+ E := Node.AsCondExpression;
+ LogFmt('CondExpression (%d pairs)', [Length(E.Pairs)], Node);
Indent;
- for i := 0 to High(Node.Pairs) do
+ for i := 0 to High(E.Pairs) do
begin
LogFmt('Pair %d:', [i]);
Indent;
Log('Condition:');
- Node.Pairs[i].Condition.Accept(Self);
+ Visit(E.Pairs[i].Condition);
Log('Branch:');
- Node.Pairs[i].Branch.Accept(Self);
+ Visit(E.Pairs[i].Branch);
Unindent;
end;
Log('Else:');
- Node.ElseBranch.Accept(Self);
+ Visit(E.ElseBranch);
Unindent;
end;
-function TAstDumper.VisitLambdaExpression(const Node: ILambdaExpressionNode): TVoid;
+function TAstDumper.VisitLambdaExpression(const Node: IAstNode): TVoid;
var
- upvalueAddr: TResolvedAddress;
+ E: ILambdaExpressionNode;
+ addr: TResolvedAddress;
symbols: TArray;
layout: IScopeLayout;
slot: Integer;
typ: IStaticType;
begin
+ E := Node.AsLambdaExpression;
LogFmt(
'LambdaExpression (HasNested: %s, IsPure: %s)',
- [Node.HasNestedLambdas.ToString(TUseBoolStrs.True), BoolToStr(Node.IsPure, True)],
+ [E.HasNestedLambdas.ToString(TUseBoolStrs.True), E.IsPure.ToString(TUseBoolStrs.True)],
Node
);
Indent;
- // 1. Layout & Symbols
- if Assigned(Node.Layout) then
+ if Assigned(E.Layout) then
begin
- LogFmt('Scope: Layout Slots=%d', [Node.Layout.SlotCount]);
- if Assigned(Node.Descriptor) then
+ LogFmt('Scope: Layout Slots=%d', [E.Layout.SlotCount]);
+ if Assigned(E.Descriptor) then
begin
Log('Symbol Table:');
Indent;
- layout := Node.Layout;
+ layout := E.Layout;
symbols := layout.GetSymbols;
TArray.Sort(symbols);
-
for var name in symbols do
begin
slot := layout.FindSlot(name);
- typ := Node.Descriptor.GetSymbolType(slot);
+ typ := E.Descriptor.GetSymbolType(slot);
LogFmt('"%s" -> Slot %d (Type: %s)', [name, slot, typ.ToString]);
end;
Unindent;
end;
- end
- else
- Log('Scope: No Layout (Raw)');
+ end;
- // 3. Parameters
Log('Parameters:');
Indent;
- // Iterate over IParameterList
- for var param in Node.Parameters do
- param.Accept(Self);
+ Visit(E.Parameters);
Unindent;
- // 4. Upvalues
- if Length(Node.Upvalues) > 0 then
+ if Length(E.Upvalues) > 0 then
begin
- LogFmt('Captured Upvalues (%d):', [Length(Node.Upvalues)]);
+ LogFmt('Captured Upvalues (%d):', [Length(E.Upvalues)]);
Indent;
- for upvalueAddr in Node.Upvalues do
- Log(FormatAddress(upvalueAddr));
+ for addr in E.Upvalues do
+ begin
+ Log(FormatAddress(addr));
+ end;
Unindent;
end;
- // 5. Body
Log('Body:');
- Node.Body.Accept(Self);
-
+ Visit(E.Body);
Unindent;
end;
-function TAstDumper.VisitFunctionCall(const Node: IFunctionCallNode): TVoid;
+function TAstDumper.VisitFunctionCall(const Node: IAstNode): TVoid;
var
- arg: IAstNode;
- staticStatus: string;
- sigStr: string;
+ C: IFunctionCallNode;
argTypes: TArray;
i: Integer;
begin
- sigStr := '';
- // Note: Node.Arguments is IArgumentList now.
- if Assigned(Node.StaticTarget) then
- begin
- staticStatus := 'Assigned';
- SetLength(argTypes, Node.Arguments.Count);
- for i := 0 to Node.Arguments.Count - 1 do
- begin
- if Node.Arguments[i].IsTyped then
- argTypes[i] := Node.Arguments[i].AsTypedNode.StaticType.ToString
- else
- argTypes[i] := 'Untyped';
- end;
- sigStr := Format(' ', [string.Join(', ', argTypes), Node.StaticType.ToString]);
- end
- else
- staticStatus := 'nil';
-
+ C := Node.AsFunctionCall;
LogFmt(
- 'FunctionCall (IsTailCall: %s, StaticTarget: %s%s, IsTargetPure: %s)',
- [Node.IsTailCall.ToString(TUseBoolStrs.True), staticStatus, sigStr, BoolToStr(Node.IsTargetPure, True)],
+ 'FunctionCall (IsTailCall: %s, StaticTarget: %s, IsTargetPure: %s)',
+ [
+ C.IsTailCall.ToString(TUseBoolStrs.True),
+ Assigned(C.StaticTarget).ToString(TUseBoolStrs.True),
+ C.IsTargetPure.ToString(TUseBoolStrs.True)
+ ],
Node
);
+ if Assigned(C.StaticTarget) then
+ begin
+ Indent;
+ SetLength(argTypes, C.Arguments.Count);
+ for i := 0 to C.Arguments.Count - 1 do
+ begin
+ if C.Arguments[i].IsTyped then
+ argTypes[i] := C.Arguments[i].AsTypedNode.StaticType.ToString
+ else
+ argTypes[i] := 'Untyped';
+ end;
+ LogFmt('ResolvedSig: Method(%s): %s', [string.Join(', ', argTypes), C.StaticType.ToString]);
+ Unindent;
+ end;
+
Indent;
Log('Callee:');
- Node.Callee.Accept(Self);
- LogFmt('Arguments (%d):', [Node.Arguments.Count]);
- Indent;
- // Iterate over IArgumentList
- for arg in Node.Arguments do
- arg.Accept(Self);
- Unindent;
+ Visit(C.Callee);
+ LogFmt('Arguments (%d):', [C.Arguments.Count]);
+ Visit(C.Arguments);
Unindent;
end;
-function TAstDumper.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TVoid;
+function TAstDumper.VisitMacroExpansionNode(const Node: IAstNode): TVoid;
var
- arg: IAstNode;
+ M: IMacroExpansionNode;
begin
+ M := Node.AsMacroExpansion;
Log('MacroExpansion', Node);
Indent;
-
Log('Original Call:');
- Indent;
- Log('Callee:');
- Node.CallNode.Callee.Accept(Self);
- LogFmt('Arguments (%d):', [Node.CallNode.Arguments.Count]);
- Indent;
- for arg in Node.CallNode.Arguments do
- arg.Accept(Self);
- Unindent;
- Unindent;
-
+ Visit(M.CallNode);
Log('Expanded Body:');
- Indent;
- Node.ExpandedBody.Accept(Self);
- Unindent;
-
+ Visit(M.ExpandedBody);
Unindent;
end;
-function TAstDumper.VisitRecurNode(const Node: IRecurNode): TVoid;
-var
- arg: IAstNode;
+function TAstDumper.VisitRecurNode(const Node: IAstNode): TVoid;
begin
Log('Recur', Node);
Indent;
- LogFmt('Arguments (%d):', [Node.Arguments.Count]);
- Indent;
- for arg in Node.Arguments do
- arg.Accept(Self);
- Unindent;
+ Visit(Node.AsRecur.Arguments);
Unindent;
end;
-function TAstDumper.VisitBlockExpression(const Node: IBlockExpressionNode): TVoid;
-var
- expr: IAstNode;
+function TAstDumper.VisitBlockExpression(const Node: IAstNode): TVoid;
begin
Log('BlockExpression', Node);
Indent;
- for expr in Node.Expressions do
- expr.Accept(Self);
+ Visit(Node.AsBlockExpression.Expressions);
Unindent;
end;
-function TAstDumper.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TVoid;
+function TAstDumper.VisitVariableDeclaration(const Node: IAstNode): TVoid;
+var
+ V: IVariableDeclarationNode;
begin
- LogFmt('VariableDeclaration (IsBoxed: %s)', [Node.IsBoxed.ToString(TUseBoolStrs.True)], Node);
+ V := Node.AsVariableDeclaration;
+ LogFmt('VariableDeclaration (IsBoxed: %s)', [V.IsBoxed.ToString(TUseBoolStrs.True)], Node);
Indent;
- Node.Target.Accept(Self);
- if Assigned(Node.Initializer) then
+ Visit(V.Target);
+ if Assigned(V.Initializer) then
begin
Log('Initializer:');
- Node.Initializer.Accept(Self);
+ Visit(V.Initializer);
end;
Unindent;
end;
-function TAstDumper.VisitAssignment(const Node: IAssignmentNode): TVoid;
+function TAstDumper.VisitAssignment(const Node: IAstNode): TVoid;
+var
+ A: IAssignmentNode;
begin
+ A := Node.AsAssignment;
Log('Assignment', Node);
Indent;
- Node.Target.Accept(Self);
+ Visit(A.Target);
Log('Value:');
- Node.Value.Accept(Self);
+ Visit(A.Value);
Unindent;
end;
-function TAstDumper.VisitMacroDefinition(const Node: IMacroDefinitionNode): TVoid;
+function TAstDumper.VisitMacroDefinition(const Node: IAstNode): TVoid;
+var
+ M: IMacroDefinitionNode;
begin
+ M := Node.AsMacroDefinition;
Log('MacroDefinition', Node);
Indent;
-
Log('Name:');
- Indent;
- Node.Name.Accept(Self);
- Unindent;
-
+ Visit(M.Name);
Log('Parameters:');
- Indent;
- for var param in Node.Parameters do
- param.Accept(Self);
- Unindent;
-
+ Visit(M.Parameters);
Log('Body:');
- Indent;
- Node.Body.Accept(Self);
- Unindent;
-
+ Visit(M.Body);
Unindent;
end;
-function TAstDumper.VisitQuasiquote(const Node: IQuasiquoteNode): TVoid;
+function TAstDumper.VisitQuasiquote(const Node: IAstNode): TVoid;
begin
Log('Quasiquote', Node);
Indent;
- Node.Expression.Accept(Self);
+ Visit(Node.AsQuasiquote.Expression);
Unindent;
end;
-function TAstDumper.VisitUnquote(const Node: IUnquoteNode): TVoid;
+function TAstDumper.VisitUnquote(const Node: IAstNode): TVoid;
begin
Log('Unquote', Node);
Indent;
- Node.Expression.Accept(Self);
+ Visit(Node.AsUnquote.Expression);
Unindent;
end;
-function TAstDumper.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TVoid;
+function TAstDumper.VisitUnquoteSplicing(const Node: IAstNode): TVoid;
begin
Log('UnquoteSplicing', Node);
Indent;
- Node.Expression.Accept(Self);
+ Visit(Node.AsUnquoteSplicing.Expression);
Unindent;
end;
-function TAstDumper.VisitIndexer(const Node: IIndexerNode): TVoid;
+function TAstDumper.VisitIndexer(const Node: IAstNode): TVoid;
+var
+ I: IIndexerNode;
begin
+ I := Node.AsIndexer;
Log('Indexer', Node);
Indent;
Log('Base:');
- Node.Base.Accept(Self);
+ Visit(I.Base);
Log('Index:');
- Node.Index.Accept(Self);
+ Visit(I.Index);
Unindent;
end;
-function TAstDumper.VisitMemberAccess(const Node: IMemberAccessNode): TVoid;
+function TAstDumper.VisitMemberAccess(const Node: IAstNode): TVoid;
+var
+ M: IMemberAccessNode;
begin
+ M := Node.AsMemberAccess;
Log('MemberAccess', Node);
Indent;
Log('Base:');
- Node.Base.Accept(Self);
+ Visit(M.Base);
Log('Member:');
- Node.Member.Accept(Self);
+ Visit(M.Member);
Unindent;
end;
-function TAstDumper.VisitRecordLiteral(const Node: IRecordLiteralNode): TVoid;
+function TAstDumper.VisitRecordLiteral(const Node: IAstNode): TVoid;
var
- field: IRecordFieldNode;
+ R: IRecordLiteralNode;
begin
- LogFmt('RecordLiteral (%d fields)', [Node.Fields.Count], Node);
+ R := Node.AsRecordLiteral;
+ LogFmt('RecordLiteral (%d fields)', [R.Fields.Count], Node);
Indent;
- // Iterate over IRecordFieldList
- for field in Node.Fields do
- begin
- LogFmt('Field :%s', [field.Key.Value.Name]);
- Indent;
- field.Value.Accept(Self);
- Unindent;
- end;
+ Visit(R.Fields);
Unindent;
end;
-function TAstDumper.VisitCreateSeries(const Node: ICreateSeriesNode): TVoid;
+function TAstDumper.VisitCreateSeries(const Node: IAstNode): TVoid;
begin
- LogFmt('CreateSeries: %s', [Node.Definition], Node);
+ LogFmt('CreateSeries: %s', [Node.AsCreateSeries.Definition], Node);
end;
-function TAstDumper.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TVoid;
+function TAstDumper.VisitAddSeriesItem(const Node: IAstNode): TVoid;
+var
+ A: IAddSeriesItemNode;
begin
+ A := Node.AsAddSeriesItem;
Log('AddSeriesItem', Node);
Indent;
Log('Series:');
- Node.Series.Accept(Self);
+ Visit(A.Series);
Log('Value:');
- Node.Value.Accept(Self);
- if Assigned(Node.Lookback) then
+ Visit(A.Value);
+ if Assigned(A.Lookback) then
begin
Log('Lookback:');
- Node.Lookback.Accept(Self);
+ Visit(A.Lookback);
end;
Unindent;
end;
-function TAstDumper.VisitSeriesLength(const Node: ISeriesLengthNode): TVoid;
+function TAstDumper.VisitSeriesLength(const Node: IAstNode): TVoid;
begin
Log('SeriesLength', Node);
Indent;
- Log('Series:');
- Node.Series.Accept(Self);
+ Visit(Node.AsSeriesLength.Series);
Unindent;
end;
-function TAstDumper.VisitNop(const Node: INopNode): TVoid;
+function TAstDumper.VisitNop(const Node: IAstNode): TVoid;
begin
Log('Nop', Node);
end;
-// --- List Visitors Implementation ---
+{ List Visitors }
-function TAstDumper.VisitParameterList(const Node: IParameterList): TVoid;
+function TAstDumper.VisitParameterList(const Node: IAstNode): TVoid;
begin
- // Usually handled by Parent Node iteration (Lambda),
- // but if visited directly:
- for var item in Node do
- item.Accept(Self);
+ for var item in Node.AsParameterList do
+ Visit(item);
end;
-function TAstDumper.VisitArgumentList(const Node: IArgumentList): TVoid;
+function TAstDumper.VisitArgumentList(const Node: IAstNode): TVoid;
begin
- for var item in Node do
- item.Accept(Self);
-end;
-
-function TAstDumper.VisitExpressionList(const Node: IExpressionList): TVoid;
-begin
- for var item in Node do
- item.Accept(Self);
-end;
-
-function TAstDumper.VisitRecordFieldList(const Node: IRecordFieldList): TVoid;
-begin
- for var item in Node do
- item.Accept(Self);
-end;
-
-function TAstDumper.VisitRecordField(const Node: IRecordFieldNode): TVoid;
-begin
- // If visited directly (e.g. inside a list)
- LogFmt('Field :%s', [Node.Key.Value.Name]);
Indent;
- Node.Value.Accept(Self);
+ for var item in Node.AsArgumentList do
+ Visit(item);
+ Unindent;
+end;
+
+function TAstDumper.VisitExpressionList(const Node: IAstNode): TVoid;
+begin
+ for var item in Node.AsExpressionList do
+ Visit(item);
+end;
+
+function TAstDumper.VisitRecordFieldList(const Node: IAstNode): TVoid;
+begin
+ for var item in Node.AsRecordFieldList do
+ Visit(item);
+end;
+
+function TAstDumper.VisitRecordField(const Node: IAstNode): TVoid;
+var
+ F: IRecordFieldNode;
+begin
+ F := Node.AsRecordField;
+ LogFmt('Field :%s', [F.Key.Value.Name]);
+ Indent;
+ Visit(F.Value);
+ Unindent;
+end;
+
+{ Pipe Visitors }
+
+function TAstDumper.VisitPipeInput(const Node: IAstNode): TVoid;
+var
+ P: IPipeInputNode;
+begin
+ P := Node.AsPipeInput;
+ Log('PipeInput', Node);
+ Indent;
+ Log('Source:');
+ Visit(P.StreamSource);
+ Log('Selectors:');
+ Visit(P.Selectors);
+ Unindent;
+end;
+
+function TAstDumper.VisitPipeSelectorList(const Node: IAstNode): TVoid;
+begin
+ for var item in Node.AsPipeSelectorList do
+ Visit(item);
+end;
+
+function TAstDumper.VisitPipeInputList(const Node: IAstNode): TVoid;
+begin
+ for var item in Node.AsPipeInputList do
+ Visit(item);
+end;
+
+function TAstDumper.VisitPipe(const Node: IAstNode): TVoid;
+var
+ P: IPipeNode;
+begin
+ P := Node.AsPipe;
+ Log('Pipe', Node);
+ Indent;
+ Log('Inputs:');
+ Visit(P.Inputs);
+ Log('Transformation:');
+ Visit(P.Transformation);
Unindent;
end;
diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas
index dbba02a..eeca98d 100644
--- a/Src/AST/Myc.Ast.Evaluator.pas
+++ b/Src/AST/Myc.Ast.Evaluator.pas
@@ -11,68 +11,58 @@ uses
Myc.Data.Keyword,
Myc.Ast.Nodes,
Myc.Ast.Scope,
- Myc.Ast.Visitor;
+ Myc.Ast;
type
- // Exception for runtime errors during script evaluation
EEvaluatorException = class(EAstException);
+ TEvaluatorFactory = reference to function(const AScope: IExecutionScope): IEvaluatorVisitor;
- // The standard AST evaluator for production use.
- TEvaluatorVisitor = class(TAstVisitor, IEvaluatorVisitor)
+ TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor, IEvaluatorVisitor)
private
FScope: IExecutionScope;
-
protected
- // --- Core Logic Implementation (Legacy Virtual Overrides) ---
- // These are kept as virtual methods so TDebugEvaluatorVisitor can still override them.
- function VisitConstant(const Node: IConstantNode): TDataValue; override;
- function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
- function VisitKeyword(const Node: IKeywordNode): TDataValue; override;
-
- function VisitParameterList(const Node: IParameterList): TDataValue; override;
- function VisitArgumentList(const Node: IArgumentList): TDataValue; override;
- function VisitExpressionList(const Node: IExpressionList): TDataValue; override;
- function VisitRecordFieldList(const Node: IRecordFieldList): TDataValue; override;
- function VisitRecordField(const Node: IRecordFieldNode): TDataValue; override;
-
- function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
- function VisitCondExpression(const Node: ICondExpressionNode): 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 VisitRecordLiteral(const Node: IRecordLiteralNode): 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;
- function VisitNop(const Node: INopNode): TDataValue; override;
-
- function VisitPipeInput(const Node: IPipeInputNode): TDataValue; override;
- function VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; override;
- function VisitPipeInputList(const Node: IPipeInputList): TDataValue; override;
- function VisitPipe(const Node: IPipeNode): TDataValue; override;
-
- function IsTruthy(const AValue: TDataValue): Boolean; inline;
-
- // Returns a closure that can create the correct type of visitor for a lambda's body.
+ function Visit(const Node: IAstNode): TDataValue; virtual;
function CreateVisitorFactory: TEvaluatorFactory; virtual;
- property Scope: IExecutionScope read FScope;
+ function VisitConstant(const N: IConstantNode): TDataValue; virtual;
+ function VisitIdentifier(const N: IIdentifierNode): TDataValue; virtual;
+ function VisitKeyword(const N: IKeywordNode): TDataValue; virtual;
+ function VisitParameterList(const N: IParameterList): TDataValue; virtual;
+ function VisitArgumentList(const N: IArgumentList): TDataValue; virtual;
+ function VisitExpressionList(const N: IExpressionList): TDataValue; virtual;
+ function VisitRecordFieldList(const N: IRecordFieldList): TDataValue; virtual;
+ function VisitRecordField(const N: IRecordFieldNode): TDataValue; virtual;
+ function VisitIfExpression(const N: IIfExpressionNode): TDataValue; virtual;
+ function VisitCondExpression(const N: ICondExpressionNode): TDataValue; virtual;
+ function VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue; virtual;
+ function VisitFunctionCall(const N: IFunctionCallNode): TDataValue; virtual;
+ function VisitMacroExpansionNode(const N: IMacroExpansionNode): TDataValue; virtual;
+ function VisitBlockExpression(const N: IBlockExpressionNode): TDataValue; virtual;
+ function VisitVariableDeclaration(const N: IVariableDeclarationNode): TDataValue; virtual;
+ function VisitAssignment(const N: IAssignmentNode): TDataValue; virtual;
+ function VisitMacroDefinition(const N: IMacroDefinitionNode): TDataValue; virtual;
+ function VisitQuasiquote(const N: IQuasiquoteNode): TDataValue; virtual;
+ function VisitUnquote(const N: IUnquoteNode): TDataValue; virtual;
+ function VisitUnquoteSplicing(const N: IUnquoteSplicingNode): TDataValue; virtual;
+ function VisitIndexer(const N: IIndexerNode): TDataValue; virtual;
+ function VisitMemberAccess(const N: IMemberAccessNode): TDataValue; virtual;
+ function VisitRecordLiteral(const N: IRecordLiteralNode): TDataValue; virtual;
+ function VisitCreateSeries(const N: ICreateSeriesNode): TDataValue; virtual;
+ function VisitAddSeriesItem(const N: IAddSeriesItemNode): TDataValue; virtual;
+ function VisitSeriesLength(const N: ISeriesLengthNode): TDataValue; virtual;
+ function VisitRecurNode(const N: IRecurNode): TDataValue; virtual;
+ function VisitNop(const N: INopNode): TDataValue; virtual;
+ function VisitPipeInput(const N: IPipeInputNode): TDataValue; virtual;
+ function VisitPipeSelectorList(const N: IPipeSelectorList): TDataValue; virtual;
+ function VisitPipeInputList(const N: IPipeInputList): TDataValue; virtual;
+ function VisitPipe(const N: IPipeNode): TDataValue; virtual;
+
+ function IsTruthy(const AValue: TDataValue): Boolean; inline;
public
constructor Create(const AScope: IExecutionScope);
-
- // Executes an AST with proper TCO handling. This is the main entry point.
function Execute(const RootNode: IAstNode): TDataValue;
class procedure HandleTCO(var ResultValue: TDataValue); static;
+ property Scope: IExecutionScope read FScope;
end;
implementation
@@ -80,7 +70,6 @@ implementation
uses
System.TypInfo,
System.Generics.Defaults,
- Myc.Ast,
Myc.Data.Decimal,
Myc.Data.Series,
Myc.Data.Stream,
@@ -88,7 +77,6 @@ uses
Myc.Data.Scalar.JSON,
Myc.Ast.Types;
-// Helper type for TCO via trampolining.
type
TThunk = record
Callee: TDataValue;
@@ -104,122 +92,123 @@ begin
Recur := ARecur;
end;
-{ TEvaluatorVisitor }
-
constructor TEvaluatorVisitor.Create(const AScope: IExecutionScope);
begin
- FScope := AScope;
- // Base constructor calls SetupHandlers automatically
inherited Create;
- Assert(Assigned(AScope));
+ FScope := AScope;
+end;
+
+function TEvaluatorVisitor.Visit(const Node: IAstNode): TDataValue;
+begin
+ case Node.Kind of
+ akConstant: Result := VisitConstant(Node.AsConstant);
+ akIdentifier: Result := VisitIdentifier(Node.AsIdentifier);
+ akKeyword: Result := VisitKeyword(Node.AsKeyword);
+ akIfExpression: Result := VisitIfExpression(Node.AsIfExpression);
+ akCondExpression: Result := VisitCondExpression(Node.AsCondExpression);
+ akLambdaExpression: Result := VisitLambdaExpression(Node.AsLambdaExpression);
+ akFunctionCall: Result := VisitFunctionCall(Node.AsFunctionCall);
+ akMacroExpansion: Result := VisitMacroExpansionNode(Node.AsMacroExpansion);
+ akBlockExpression: Result := VisitBlockExpression(Node.AsBlockExpression);
+ akVariableDeclaration: Result := VisitVariableDeclaration(Node.AsVariableDeclaration);
+ akAssignment: Result := VisitAssignment(Node.AsAssignment);
+ akIndexer: Result := VisitIndexer(Node.AsIndexer);
+ akMemberAccess: Result := VisitMemberAccess(Node.AsMemberAccess);
+ akRecordLiteral: Result := VisitRecordLiteral(Node.AsRecordLiteral);
+ akCreateSeries: Result := VisitCreateSeries(Node.AsCreateSeries);
+ akAddSeriesItem: Result := VisitAddSeriesItem(Node.AsAddSeriesItem);
+ akSeriesLength: Result := VisitSeriesLength(Node.AsSeriesLength);
+ akRecur: Result := VisitRecurNode(Node.AsRecur);
+ akNop: Result := VisitNop(Node.AsNop);
+ akPipe: Result := VisitPipe(Node.AsPipe);
+ akExpressionList: Result := VisitExpressionList(Node.AsExpressionList);
+ else
+ Result := TDataValue.Void;
+ end;
+end;
+
+class procedure TEvaluatorVisitor.HandleTCO(var ResultValue: TDataValue);
+begin
+ while ResultValue.Kind = vkGeneric do
+ begin
+ var thunk := ResultValue.AsGeneric;
+ var callee := thunk.Callee.AsMethod();
+ ResultValue := callee(thunk.Args);
+ end;
end;
function TEvaluatorVisitor.Execute(const RootNode: IAstNode): TDataValue;
begin
if not Assigned(RootNode) then
exit(TDataValue.Void);
-
try
- // Call the central dispatch method from TAstVisitor
Result := Visit(RootNode);
HandleTCO(Result);
- except
- on E: EAstException do
- raise; // Already a compiler/runtime exception, pass through
- on E: Exception do
- // Wrap unexpected RTL exceptions (e.g. EDivByZero, EConvertError)
- raise EEvaluatorException.Create('Runtime Error: ' + E.Message);
- end;
-end;
-
-function TEvaluatorVisitor.CreateVisitorFactory: TEvaluatorFactory;
-begin
- // The production visitor returns a factory that creates another production visitor.
- Result := function(const AScope: IExecutionScope): IEvaluatorVisitor begin Result := TEvaluatorVisitor.Create(AScope); end;
-end;
-
-class procedure TEvaluatorVisitor.HandleTCO(var ResultValue: TDataValue);
-begin
- // This is the central trampoline loop for Tail Call Optimization.
- try
- while ResultValue.Kind = vkGeneric do
- begin
- var thunk := ResultValue.AsGeneric;
- var callee := thunk.Callee.AsMethod();
- ResultValue := callee(thunk.Args);
- end;
except
on E: EAstException do
raise;
on E: Exception do
- raise EEvaluatorException.Create('Runtime Error (TCO): ' + E.Message);
+ raise EEvaluatorException.Create('Runtime: ' + E.Message);
end;
end;
function TEvaluatorVisitor.IsTruthy(const AValue: TDataValue): Boolean;
begin
- // Other types (Text, Series, etc.) are considered "false" in a boolean context
if (AValue.Kind <> vkScalar) then
exit(false);
-
case AValue.AsScalar.Kind of
- TScalar.TKind.Ordinal: Result := AValue.AsScalar.Value.AsInt64 <> 0;
- TScalar.TKind.Float: Result := AValue.AsScalar.Value.AsDouble <> 0.0;
- TScalar.TKind.Keyword: Result := AValue.AsScalar.Value.AsInt64 <> 0;
- TScalar.TKind.Boolean: Result := AValue.AsScalar.Value.AsInt64 <> 0;
- TScalar.TKind.DateTime: Result := AValue.AsScalar.Value.AsDouble <> 0.0;
+ TScalar.TKind.Ordinal, TScalar.TKind.Keyword, TScalar.TKind.Boolean: Result := AValue.AsScalar.Value.AsInt64 <> 0;
+ TScalar.TKind.Float, TScalar.TKind.DateTime: Result := AValue.AsScalar.Value.AsDouble <> 0.0;
else
Result := false;
end;
end;
-// --- Visits Implementation ---
-
-function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
+function TEvaluatorVisitor.CreateVisitorFactory: TEvaluatorFactory;
begin
- Result := Node.Value;
+ Result := function(const AScope: IExecutionScope): IEvaluatorVisitor begin Result := TEvaluatorVisitor.Create(AScope); end;
end;
-function TEvaluatorVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue;
+// --- Implementations ---
+
+function TEvaluatorVisitor.VisitConstant(const N: IConstantNode): TDataValue;
begin
- Result := TDataValue(TScalar.FromKeyword(Node.Value));
+ Result := N.Value;
end;
-function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
+function TEvaluatorVisitor.VisitKeyword(const N: IKeywordNode): TDataValue;
begin
- Result := FScope[Node.Address];
+ Result := TDataValue(TScalar.FromKeyword(N.Value));
end;
-function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
+function TEvaluatorVisitor.VisitIdentifier(const N: IIdentifierNode): TDataValue;
+begin
+ Result := FScope[N.Address];
+end;
+
+function TEvaluatorVisitor.VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue;
var
capturedCells: TArray;
i: Integer;
closureScope: IExecutionScope;
visitorFactory: TEvaluatorFactory;
begin
- // 1. Capture Upvalues
- if Node.Upvalues <> nil then
+ if Length(N.Upvalues) > 0 then
begin
- SetLength(capturedCells, Length(Node.Upvalues));
- for i := 0 to High(Node.Upvalues) do
- capturedCells[i] := FScope.Capture(Node.Upvalues[i]);
+ SetLength(capturedCells, Length(N.Upvalues));
+ for i := 0 to High(N.Upvalues) do
+ capturedCells[i] := FScope.Capture(N.Upvalues[i]);
end
else
capturedCells := nil;
- // 2. Determine Parent Scope for Closure
- if Node.HasNestedLambdas then
- closureScope := FScope
- else
- closureScope := nil;
-
- // 3. Prepare Visitor Factory
+ closureScope :=
+ if N.HasNestedLambdas then FScope
+ else nil;
visitorFactory := CreateVisitorFactory();
- // 4. Capture Metadata for Closure
- var descriptor := Node.Descriptor;
- var params := Node.Parameters;
- var cNode: ILambdaExpressionNode := Node;
+ var descriptor := N.Descriptor;
+ var params := N.Parameters;
var [unsafe] closure: TDataValue.TFunc;
closure :=
@@ -227,93 +216,51 @@ begin
var
lambdaScope: IExecutionScope;
bodyVisitor: IAstVisitor;
- i: Integer;
- adr: TResolvedAddress;
+ k: Integer;
begin
if (Length(ArgValues) <> params.Count) then
- raise EEvaluatorException.CreateFmt('Argument count mismatch: expected %d, got %d', [params.Count, Length(ArgValues)]);
-
- // Create the new execution scope for this function call.
+ raise EEvaluatorException.Create('Arg mismatch');
lambdaScope := TScope.CreateScope(closureScope, descriptor, capturedCells);
- // Capture the closure itself in slot 0 for 'recur' to find it (if needed).
- adr.Kind := akLocalOrParent;
- adr.ScopeDepth := 0;
- adr.SlotIndex := 0;
- lambdaScope[adr] := TDataValue(closure);
+ // Self-reference for recursion
+ lambdaScope.SetValues(TResolvedAddress.Create(akLocalOrParent, 0, 0), TDataValue(closure));
- // Populate the scope with the actual parameters passed to the function.
- for i := 0 to params.Count - 1 do
- begin
- adr := params[i].Address;
- Assert(adr.ScopeDepth = 0);
- lambdaScope[adr] := ArgValues[i];
- end;
+ for k := 0 to params.Count - 1 do
+ lambdaScope[params[k].Address] := ArgValues[k];
- // Create a visitor with the new scope and execute the lambda's body.
- // Use the generic Visit method for dispatch
bodyVisitor := visitorFactory(lambdaScope);
- Result := bodyVisitor.Visit(cNode.Body);
+ Result := bodyVisitor.Visit(N.Body);
end;
-
Result := TDataValue(closure);
end;
-function TEvaluatorVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
-begin
- Result := TDataValue.Void;
-end;
-
-function TEvaluatorVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
-begin
- raise EEvaluatorException.Create('Quasiquote nodes are a compile-time construct and cannot be evaluated at runtime.');
-end;
-
-function TEvaluatorVisitor.VisitUnquote(const Node: IUnquoteNode): TDataValue;
-begin
- raise EEvaluatorException.Create('Unquote nodes are a compile-time construct and cannot be evaluated at runtime.');
-end;
-
-function TEvaluatorVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
-begin
- raise EEvaluatorException.Create('Unquote-splicing nodes are a compile-time construct and cannot be evaluated at runtime.');
-end;
-
-function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
+function TEvaluatorVisitor.VisitFunctionCall(const N: IFunctionCallNode): TDataValue;
var
calleeValue: TDataValue;
argValues: TArray;
i: Integer;
- argList: IArgumentList;
begin
- if Assigned(Node.StaticTarget) then
+ if Assigned(N.StaticTarget) then
begin
- // --- Static Path (Optimized) ---
- argList := Node.Arguments;
- SetLength(argValues, argList.Count);
- for i := 0 to argList.Count - 1 do
- argValues[i] := Visit(argList[i]); // Use central dispatch
+ SetLength(argValues, N.Arguments.Count);
+ for i := 0 to N.Arguments.Count - 1 do
+ argValues[i] := Visit(N.Arguments[i]);
- // Call static target. Exceptions here are caught by Execute/HandleTCO.
- Result := Node.StaticTarget(argValues);
- HandleTCO(Result);
+ Result := N.StaticTarget(argValues);
+ if not N.IsTailCall then
+ HandleTCO(Result); // Flushes stack if not tail
end
else
begin
- // --- Dynamic Path (Default) ---
- calleeValue := Visit(Node.Callee);
+ calleeValue := Visit(N.Callee);
if calleeValue.Kind <> vkMethod then
- raise EEvaluatorException.Create('Expression is not invokable in this context.');
+ raise EEvaluatorException.Create('Not a function');
+ SetLength(argValues, N.Arguments.Count);
+ for i := 0 to N.Arguments.Count - 1 do
+ argValues[i] := Visit(N.Arguments[i]);
- argList := Node.Arguments;
- SetLength(argValues, argList.Count);
- for i := 0 to argList.Count - 1 do
- argValues[i] := Visit(argList[i]);
-
- if Node.IsTailCall then
- begin
- Result := TDataValue.FromGeneric(TThunk.Create(calleeValue, argValues, false));
- end
+ if N.IsTailCall then
+ Result := TDataValue.FromGeneric(TThunk.Create(calleeValue, argValues, false))
else
begin
Result := (calleeValue.AsMethod)(argValues);
@@ -322,339 +269,170 @@ begin
end;
end;
-function TEvaluatorVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TDataValue;
-begin
- Result := Visit(Node.ExpandedBody);
-end;
-
-function TEvaluatorVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue;
+function TEvaluatorVisitor.VisitRecurNode(const N: IRecurNode): TDataValue;
var
argValues: TArray;
- calleeAddress: TResolvedAddress;
- calleeValue: TDataValue;
i: Integer;
begin
- SetLength(argValues, Node.Arguments.Count);
- for i := 0 to Node.Arguments.Count - 1 do
- argValues[i] := Visit(Node.Arguments[i]);
-
- calleeAddress.Kind := akLocalOrParent;
- calleeAddress.ScopeDepth := 0;
- calleeAddress.SlotIndex := 0;
- calleeValue := FScope[calleeAddress];
-
- Result := TDataValue.FromGeneric(TThunk.Create(calleeValue, argValues, true));
+ SetLength(argValues, N.Arguments.Count);
+ for i := 0 to N.Arguments.Count - 1 do
+ argValues[i] := Visit(N.Arguments[i]);
+ // The "self" function is always at Slot 0
+ var callee := FScope[TResolvedAddress.Create(akLocalOrParent, 0, 0)];
+ Result := TDataValue.FromGeneric(TThunk.Create(callee, argValues, true));
end;
-function TEvaluatorVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
-var
- itemValue, lookbackValue, seriesVar: TDataValue;
- lookback: Int64;
+function TEvaluatorVisitor.VisitBlockExpression(const N: IBlockExpressionNode): TDataValue;
begin
- seriesVar := FScope[Node.Series.Address];
- itemValue := Visit(Node.Value);
- lookback := -1;
+ Result := Visit(N.Expressions);
+end;
- if Assigned(Node.Lookback) then
- begin
- lookbackValue := Visit(Node.Lookback);
- if (lookbackValue.Kind <> vkScalar) or (lookbackValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then
- raise EEvaluatorException.Create('Lookback parameter must be an integer.');
- lookback := lookbackValue.AsScalar.Value.AsInt64;
- end;
-
- case seriesVar.Kind of
- vkRecordSeries:
- begin
- if (itemValue.Kind <> vkScalarRecord) then
- raise EEvaluatorException.Create('Can only add record values to a TScalarRecordSeries.');
-
- seriesVar.AsRecordSeries.Add(itemValue.AsScalarRecord, lookback);
- end;
- else
- raise EEvaluatorException.Create('"add" operation is only supported for series types.');
- end;
+function TEvaluatorVisitor.VisitExpressionList(const N: IExpressionList): TDataValue;
+var
+ i: Integer;
+begin
Result := TDataValue.Void;
+ for i := 0 to N.Count - 1 do
+ Result := Visit(N[i]);
end;
-function TEvaluatorVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue;
+function TEvaluatorVisitor.VisitIfExpression(const N: IIfExpressionNode): TDataValue;
begin
- if Node.Target.Kind <> akIdentifier then
- raise EEvaluatorException.Create('Runtime Error: Assignment target must be an identifier.');
-
- Result := Visit(Node.Value);
- FScope[Node.Target.AsIdentifier.Address] := Result;
-end;
-
-function TEvaluatorVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
-var
- def: string;
-begin
- def := Node.Definition.Trim;
- try
- if def.StartsWith('[') then
- begin
- var recordDef := TRttiAstHelper.JsonToRecordDefinition(def);
- if recordDef.Count = 0 then
- raise EEvaluatorException.Create('Failed to parse record definition from JSON array.');
- var recordSeries := TScalarRecordSeries.Create(recordDef);
- Result := TDataValue.FromRecordSeries(recordSeries);
- end
- else
- begin
- var scalarKind := TScalar.StringToKind(def);
- Result := TDataValue.FromSeries(TScalarSeries.Create(scalarKind));
- end;
- except
- on E: EAstException do
- raise;
- on E: Exception do
- raise EEvaluatorException.Create('Invalid series definition: ' + E.Message);
- end;
-end;
-
-function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
-var
- baseValue, indexValue: TDataValue;
- index: Int64;
- series: ISeries;
- recSeries: IScalarRecordSeries;
- i, fieldCount: Integer;
- values: TArray;
- key: IKeyword;
- memberSeries: ISeries;
- scalarValue: TScalar;
-begin
- baseValue := Visit(Node.Base);
-
- if baseValue.IsVoid then
- exit(TDataValue.Void);
-
- indexValue := Visit(Node.Index);
-
- if (indexValue.Kind <> vkScalar) or (indexValue.AsScalar.Kind <> TScalar.TKind.Ordinal) then
- raise EEvaluatorException.Create('Indexer `[]` requires an integer argument.');
-
- index := indexValue.AsScalar.Value.AsInt64;
-
- case baseValue.Kind of
- vkSeries:
- begin
- series := baseValue.AsSeries;
- if (index < 0) or (index >= series.TotalCount) then
- raise EEvaluatorException.CreateFmt('Index %d is out of bounds for series with %d elements.', [index, series.TotalCount]);
- Result := TDataValue(series.Items[Integer(index)]);
- end;
- vkRecordSeries:
- begin
- recSeries := baseValue.AsRecordSeries;
-
- fieldCount := recSeries.Def.Count;
- SetLength(values, fieldCount);
-
- for i := 0 to fieldCount - 1 do
- begin
- key := recSeries.Def.Items[i].Key;
- memberSeries := recSeries.Fields[key];
-
- if (index < 0) or (index >= memberSeries.TotalCount) then
- raise EEvaluatorException
- .CreateFmt('Index %d is out of bounds for series with %d elements.', [index, memberSeries.TotalCount]);
-
- scalarValue := memberSeries[index];
- values[i] := scalarValue.Value;
- end;
-
- Result := TDataValue.FromScalarRecord(TScalarRecord.Create(recSeries.Def, values));
- end;
- else
- raise EEvaluatorException.Create('Indexer `[]` is not supported for this value type.');
- end;
-end;
-
-function TEvaluatorVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
-var
- baseValue: TDataValue;
-begin
- baseValue := Visit(Node.Base);
-
- if baseValue.IsVoid then
- exit(TDataValue.Void);
-
- case baseValue.Kind of
- vkRecordSeries: Result := TDataValue.FromSeries(baseValue.AsRecordSeries.Fields[Node.Member.Value]);
- vkScalarRecord: Result := TDataValue(baseValue.AsScalarRecord.Fields[Node.Member.Value]);
- vkGenericRecord: Result := TDataValue(baseValue.AsGenericRecord.Fields[Node.Member.Value]);
- vkStream: Result := TDataValue.FromSeries(baseValue.AsStream.Series.Fields[Node.Member.Value]);
- else
- raise EEvaluatorException.Create('Member access operator `.` is not supported for this value type.');
- end;
-end;
-
-function TEvaluatorVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
-var
- i: Integer;
-begin
- if Assigned(Node.GenericDefinition) then
- begin
- var genFields: TArray>;
- SetLength(genFields, Node.Fields.Count);
-
- for i := 0 to Node.Fields.Count - 1 do
- begin
- var field := Node.Fields[i];
- genFields[i] := TPair.Create(field.Key.Value, Visit(field.Value));
- end;
-
- Result := TDataValue.FromGenericRecord(TKeywordMapping.Create(genFields));
- end
- else if Assigned(Node.ScalarDefinition) then
- begin
- var values: TArray;
- SetLength(values, Node.Fields.Count);
-
- for i := 0 to Node.Fields.Count - 1 do
- begin
- var field := Node.Fields[i];
- var valData := Visit(field.Value);
- values[i] := valData.AsScalar.Value;
- end;
-
- Result := TDataValue.FromScalarRecord(TScalarRecord.Create(Node.ScalarDefinition, values));
- end
- else
- raise EEvaluatorException.Create('RecordLiteral has no definition (Binder/TypeChecker failure).');
-end;
-
-function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
-var
- address: TResolvedAddress;
-begin
- if Node.Target.Kind <> akIdentifier then
- raise EEvaluatorException.Create('Runtime Error: Variable declaration target must be an identifier.');
-
- if Assigned(Node.Initializer) then
- Result := Visit(Node.Initializer)
- else
- Result := TDataValue.Void;
-
- address := Node.Target.AsIdentifier.Address;
-
- if Node.IsBoxed then
- begin
- Assert(address.ScopeDepth = 0);
- FScope.DefineBoxed(address.SlotIndex, Result);
- end
- else
- begin
- FScope[address] := Result;
- end;
-end;
-
-function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
-begin
- if IsTruthy(Visit(Node.Condition)) then
- Result := Visit(Node.ThenBranch)
- else if Assigned(Node.ElseBranch) then
- Result := Visit(Node.ElseBranch)
+ if IsTruthy(Visit(N.Condition)) then
+ Result := Visit(N.ThenBranch)
+ else if Assigned(N.ElseBranch) then
+ Result := Visit(N.ElseBranch)
else
Result := TDataValue.Void;
end;
-function TEvaluatorVisitor.VisitCondExpression(const Node: ICondExpressionNode): TDataValue;
-var
- pair: TCondPair;
-begin
- for pair in Node.Pairs do
- begin
- if IsTruthy(Visit(pair.Condition)) then
- begin
- Result := Visit(pair.Branch);
- Exit;
- end;
- end;
- // Else branch is mandatory in AST (though can be Nop/Void in source if omitted and normalized)
- Result := Visit(Node.ElseBranch);
-end;
-
-function TEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
-begin
- // Delegate to ExpressionList visitor via central Visit dispatch (default handler maps to VisitExpressionList)
- Result := Visit(Node.Expressions);
-end;
-
-function TEvaluatorVisitor.VisitExpressionList(const Node: IExpressionList): TDataValue;
+function TEvaluatorVisitor.VisitCondExpression(const N: ICondExpressionNode): TDataValue;
var
i: Integer;
begin
- Result := TDataValue.Void;
- for i := 0 to Node.Count - 1 do
- Result := Visit(Node[i]);
+ for i := 0 to High(N.Pairs) do
+ if IsTruthy(Visit(N.Pairs[i].Condition)) then
+ exit(Visit(N.Pairs[i].Branch));
+ Result := Visit(N.ElseBranch);
end;
-function TEvaluatorVisitor.VisitNop(const Node: INopNode): TDataValue;
-begin
- Result := TDataValue.Void;
-end;
-
-function TEvaluatorVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
+function TEvaluatorVisitor.VisitVariableDeclaration(const N: IVariableDeclarationNode): TDataValue;
var
- seriesValue: TDataValue;
- len: Int64;
+ ident: IIdentifierNode;
begin
- seriesValue := FScope[Node.Series.Address];
-
- case seriesValue.Kind of
- vkSeries: len := seriesValue.AsSeries.Count;
- vkRecordSeries: len := seriesValue.AsRecordSeries.RecordCount;
+ // 1. Initialisator auswerten
+ if Assigned(N.Initializer) then
+ Result := Visit(N.Initializer)
else
- raise EEvaluatorException
- .CreateFmt('Cannot get length of type %s.', [GetEnumName(TypeInfo(TDataValueKind), Ord(seriesValue.Kind))]);
- end;
+ Result := TDataValue.Void;
+ // 2. Das Target-Identifier-Interface extrahieren
+ // N.Target ist IAstNode, wir brauchen IIdentifierNode für die Adresse
+ ident := N.Target.AsIdentifier;
+
+ // 3. Im Scope speichern
+ if N.IsBoxed then
+ FScope.DefineBoxed(ident.Address.SlotIndex, Result)
+ else
+ FScope[ident.Address] := Result;
+end;
+
+function TEvaluatorVisitor.VisitAssignment(const N: IAssignmentNode): TDataValue;
+begin
+ Result := Visit(N.Value);
+ FScope[N.Target.AsIdentifier.Address] := Result;
+end;
+
+function TEvaluatorVisitor.VisitIndexer(const N: IIndexerNode): TDataValue;
+var
+ base, idx: TDataValue;
+begin
+ base := Visit(N.Base);
+ if base.IsVoid then
+ exit(TDataValue.Void);
+ idx := Visit(N.Index);
+ if base.Kind = vkSeries then
+ Result := TDataValue(base.AsSeries.Items[Integer(idx.AsScalar.Value.AsInt64)])
+ else if base.Kind = vkRecordSeries then
+ begin
+ var rs := base.AsRecordSeries;
+ var vals: TArray;
+ SetLength(vals, rs.Def.Count);
+ var i64 := idx.AsScalar.Value.AsInt64;
+ for var k := 0 to rs.Def.Count - 1 do
+ vals[k] := rs.Fields[rs.Def.Items[k].Key].Items[Integer(i64)].Value;
+ Result := TDataValue.FromScalarRecord(TScalarRecord.Create(rs.Def, vals));
+ end
+ else
+ raise EEvaluatorException.Create('Indexer error');
+end;
+
+function TEvaluatorVisitor.VisitMemberAccess(const N: IMemberAccessNode): TDataValue;
+var
+ base: TDataValue;
+begin
+ base := Visit(N.Base);
+ if base.IsVoid then
+ exit(TDataValue.Void);
+ case base.Kind of
+ vkRecordSeries: Result := TDataValue.FromSeries(base.AsRecordSeries.Fields[N.Member.Value]);
+ vkScalarRecord: Result := TDataValue(base.AsScalarRecord.Fields[N.Member.Value]);
+ vkGenericRecord: Result := TDataValue(base.AsGenericRecord.Fields[N.Member.Value]);
+ vkStream: Result := TDataValue.FromSeries(base.AsStream.Series.Fields[N.Member.Value]);
+ else
+ raise EEvaluatorException.Create('Member error');
+ end;
+end;
+
+function TEvaluatorVisitor.VisitRecordLiteral(const N: IRecordLiteralNode): TDataValue;
+var
+ i: Integer;
+begin
+ if Assigned(N.ScalarDefinition) then
+ begin
+ var vals: TArray;
+ SetLength(vals, N.Fields.Count);
+ for i := 0 to N.Fields.Count - 1 do
+ vals[i] := Visit(N.Fields[i].Value).AsScalar.Value;
+ Result := TDataValue.FromScalarRecord(TScalarRecord.Create(N.ScalarDefinition, vals));
+ end
+ else
+ begin
+ var fields: TArray>;
+ SetLength(fields, N.Fields.Count);
+ for i := 0 to N.Fields.Count - 1 do
+ fields[i] := TPair.Create(N.Fields[i].Key.Value, Visit(N.Fields[i].Value));
+ Result := TDataValue.FromGenericRecord(TKeywordMapping.Create(fields));
+ end;
+end;
+
+function TEvaluatorVisitor.VisitCreateSeries(const N: ICreateSeriesNode): TDataValue;
+begin
+ if N.Definition.Trim.StartsWith('[') then
+ Result := TDataValue.FromRecordSeries(TScalarRecordSeries.Create(TRttiAstHelper.JsonToRecordDefinition(N.Definition)))
+ else
+ Result := TDataValue.FromSeries(TScalarSeries.Create(TScalar.StringToKind(N.Definition)));
+end;
+
+function TEvaluatorVisitor.VisitAddSeriesItem(const N: IAddSeriesItemNode): TDataValue;
+begin
+ var lb := -1;
+ if Assigned(N.Lookback) then
+ lb := Visit(N.Lookback).AsScalar.Value.AsInt64;
+ FScope[N.Series.Address].AsRecordSeries.Add(Visit(N.Value).AsScalarRecord, lb);
+ Result := TDataValue.Void;
+end;
+
+function TEvaluatorVisitor.VisitSeriesLength(const N: ISeriesLengthNode): TDataValue;
+begin
+ var s := FScope[N.Series.Address];
+ var len :=
+ if s.Kind = vkSeries then s.AsSeries.Count
+ else s.AsRecordSeries.RecordCount;
Result := TDataValue(TScalar.FromInt64(len));
end;
-// --- List Visitor Stubs (Default to Void if not implemented specifically) ---
-
-function TEvaluatorVisitor.VisitParameterList(const Node: IParameterList): TDataValue;
-begin
- Result := TDataValue.Void;
-end;
-
-function TEvaluatorVisitor.VisitArgumentList(const Node: IArgumentList): TDataValue;
-begin
- Result := TDataValue.Void;
-end;
-
-function TEvaluatorVisitor.VisitRecordFieldList(const Node: IRecordFieldList): TDataValue;
-begin
- Result := TDataValue.Void;
-end;
-
-function TEvaluatorVisitor.VisitRecordField(const Node: IRecordFieldNode): TDataValue;
-begin
- Result := TDataValue.Void;
-end;
-
-function TEvaluatorVisitor.VisitPipeInput(const Node: IPipeInputNode): TDataValue;
-begin
- Result := TDataValue.Void;
-end;
-
-function TEvaluatorVisitor.VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue;
-begin
- Result := TDataValue.Void;
-end;
-
-function TEvaluatorVisitor.VisitPipeInputList(const Node: IPipeInputList): TDataValue;
-begin
- Result := TDataValue.Void;
-end;
-
-// --- Pipe Implementation ---
-
-function TEvaluatorVisitor.VisitPipe(const Node: IPipeNode): TDataValue;
+function TEvaluatorVisitor.VisitPipe(const N: IPipeNode): TDataValue;
var
i: Integer;
inputNode: IPipeInputNode;
@@ -662,104 +440,102 @@ var
config: TPipeConfig;
inputVal: TDataValue;
sourceSeries: IScalarRecordSeries;
- outputDef: IScalarRecordDefinition;
lambdaFunc: TDataValue.TFunc;
- pipeAdapter: TPipeStream.TPipeLambda;
begin
- SetLength(sources, Node.Inputs.Count);
- SetLength(config, Node.Inputs.Count);
-
- // 1. Resolve Inputs & Build Config
- for i := 0 to Node.Inputs.Count - 1 do
+ SetLength(sources, N.Inputs.Count);
+ SetLength(config, N.Inputs.Count);
+ for i := 0 to N.Inputs.Count - 1 do
begin
- inputNode := Node.Inputs[i];
+ inputNode := N.Inputs[i];
inputVal := FScope[inputNode.StreamSource.Address];
-
- // STRICT CHECK: Only vkStream is allowed.
- if inputVal.Kind = vkStream then
- begin
- sources[i] := inputVal.AsStream;
- end
- else
- begin
- raise EEvaluatorException
- .CreateFmt('Variable "%s" must be a Stream. Found type "%s".', [inputNode.StreamSource.Name, inputVal.Kind.ToString]);
- end;
-
+ if inputVal.Kind <> vkStream then
+ raise EEvaluatorException.Create('Stream expected');
+ sources[i] := inputVal.AsStream;
sourceSeries := sources[i].Series;
var srcDef := sourceSeries.Def;
var selectors := inputNode.Selectors;
-
SetLength(config[i], selectors.Count);
for var k := 0 to selectors.Count - 1 do
begin
var key := selectors[k].Value;
var idx := srcDef.IndexOf(key);
- // Runtime safety check (TypeChecker usually handles this)
- if idx < 0 then
- raise EEvaluatorException.CreateFmt('Field ":%s" missing in stream "%s".', [key.Name, inputNode.StreamSource.Name]);
-
- var fieldKind := srcDef.Items[idx].Value;
- config[i][k] := TScalarRecordField.Create(key, fieldKind);
+ config[i][k] := TScalarRecordField.Create(key, srcDef.Items[idx].Value);
end;
end;
-
- // 2. Get Output Definition
- if (Node.StaticType = nil) or (Node.StaticType.Kind <> stRecordSeries) then
- raise EEvaluatorException.Create(
- 'Internal Error: Pipe requires a RecordSeries static type definition. Lambda must return a Record.');
-
- outputDef := Node.StaticType.Definition;
-
- // 3. Compile Lambda
- lambdaFunc := Visit(Node.Transformation).AsMethod();
-
- // 4. Create Adapter
- pipeAdapter :=
- function(const SourceSeries: array of ISeries; out Results: array of TScalar.TValue): Boolean
+ lambdaFunc := Visit(N.Transformation).AsMethod();
+ var outputDef := N.StaticType.Definition;
+ var pipeAdapter: TPipeStream.TPipeLambda :=
+ function(const S: array of ISeries; out R: array of TScalar.TValue): Boolean
var
- scriptArgs: TArray;
- scriptResult: TDataValue;
+ args: TArray;
+ res: TDataValue;
k: Integer;
- resRec: IScalarRecord;
begin
- SetLength(scriptArgs, Length(SourceSeries));
- // Pass Scalars (Current Items)
- for k := 0 to High(SourceSeries) do
- scriptArgs[k] := TDataValue(SourceSeries[k].Items[0]);
-
- scriptResult := lambdaFunc(scriptArgs);
-
- // Void -> Filtered out (no emit)
- if scriptResult.IsVoid then
+ SetLength(args, Length(S));
+ for k := 0 to High(S) do
+ args[k] := TDataValue(S[k].Items[0]);
+ res := lambdaFunc(args);
+ if res.IsVoid then
exit(False);
-
- // STRICT CHECK: Only ScalarRecord allowed
- if scriptResult.Kind = vkScalarRecord then
- begin
- resRec := scriptResult.AsScalarRecord;
-
- // Integrity check: Runtime Record must match Static Definition
- if resRec.Def.Count <> Length(Results) then
- raise EEvaluatorException.Create('Pipe lambda returned record with incorrect field count/layout.');
-
- for k := 0 to resRec.Def.Count - 1 do
- Results[k] := resRec.Items[k].Value.Value;
- end
- else
- begin
- // Scalars are forbidden
- raise EEvaluatorException
- .CreateFmt('Pipe lambda returned invalid type "%s". Must return a Record or Void.', [scriptResult.Kind.ToString]);
- end;
-
+ var rec := res.AsScalarRecord;
+ for k := 0 to rec.Def.Count - 1 do
+ R[k] := rec.Items[k].Value.Value;
Result := True;
end;
+ Result := TDataValue.FromStream(TPipeStream.Create(config, outputDef, sources, pipeAdapter));
+end;
- var pipe := TPipeStream.Create(config, outputDef, sources, pipeAdapter);
-
- // Result is a Stream
- Result := TDataValue.FromStream(pipe);
+function TEvaluatorVisitor.VisitMacroDefinition(const N: IMacroDefinitionNode): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitQuasiquote(const N: IQuasiquoteNode): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitUnquote(const N: IUnquoteNode): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitUnquoteSplicing(const N: IUnquoteSplicingNode): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitNop(const N: INopNode): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitParameterList(const N: IParameterList): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitArgumentList(const N: IArgumentList): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitRecordFieldList(const N: IRecordFieldList): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitRecordField(const N: IRecordFieldNode): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitPipeInput(const N: IPipeInputNode): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitPipeSelectorList(const N: IPipeSelectorList): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitPipeInputList(const N: IPipeInputList): TDataValue;
+begin
+ Result := TDataValue.Void;
+end;
+function TEvaluatorVisitor.VisitMacroExpansionNode(const N: IMacroExpansionNode): TDataValue;
+begin
+ Result := Visit(N.ExpandedBody);
end;
end.
diff --git a/Src/AST/Myc.Ast.JSON.pas b/Src/AST/Myc.Ast.JSON.pas
index 97cdf3a..5a53d9c 100644
--- a/Src/AST/Myc.Ast.JSON.pas
+++ b/Src/AST/Myc.Ast.JSON.pas
@@ -20,19 +20,19 @@ type
// TJsonAstConverter implements the visitor pattern for serialization
// and uses factory functions for deserialization.
- // Inherits from the new generic visitor base class
TJsonAstConverter = class(TAstVisitor, IJsonAstConverter)
private
procedure DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string);
- function JsonToNode(const AJson: TJSONValue; const ExpectedType: String = ''): IAstNode;
+ function JsonToNode(const AJson: TJSONValue; const ExpectedType: string = ''): IAstNode;
function JsonToDataValue(const AObj: TJSONObject; const AName: string): TDataValue;
+ // Deserialization Helpers
function JsonToConstantNode(const AObj: TJSONObject): IConstantNode;
function JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode;
function JsonToKeywordNode(const AObj: TJSONObject): IKeywordNode;
function JsonToIfExprNode(const AObj: TJSONObject): IIfExpressionNode;
function JsonToCondExprNode(const AObj: TJSONObject): ICondExpressionNode;
- function JsonToTernaryExprNode(const AObj: TJSONObject): IAstNode; // Backward Compatibility
+ function JsonToTernaryExprNode(const AObj: TJSONObject): IAstNode;
function JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode;
function JsonToMacroDefNode(const AObj: TJSONObject): IMacroDefinitionNode;
function JsonToQuasiquoteNode(const AObj: TJSONObject): IQuasiquoteNode;
@@ -52,44 +52,51 @@ type
function JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode;
function JsonToNopNode(const AObj: TJSONObject): INopNode;
+ strict private
+ // Serialization Visitors (IAstNode signature)
+ function VisitConstant(const Node: IAstNode): TJSONObject;
+ function VisitIdentifier(const Node: IAstNode): TJSONObject;
+ function VisitKeyword(const Node: IAstNode): TJSONObject;
+
+ function VisitParameterList(const Node: IAstNode): TJSONObject;
+ function VisitArgumentList(const Node: IAstNode): TJSONObject;
+ function VisitExpressionList(const Node: IAstNode): TJSONObject;
+ function VisitRecordFieldList(const Node: IAstNode): TJSONObject;
+ function VisitRecordField(const Node: IAstNode): TJSONObject;
+
+ function VisitIfExpression(const Node: IAstNode): TJSONObject;
+ function VisitCondExpression(const Node: IAstNode): TJSONObject;
+ function VisitLambdaExpression(const Node: IAstNode): TJSONObject;
+ function VisitMacroDefinition(const Node: IAstNode): TJSONObject;
+ function VisitQuasiquote(const Node: IAstNode): TJSONObject;
+ function VisitUnquote(const Node: IAstNode): TJSONObject;
+ function VisitUnquoteSplicing(const Node: IAstNode): TJSONObject;
+ function VisitFunctionCall(const Node: IAstNode): TJSONObject;
+ function VisitMacroExpansionNode(const Node: IAstNode): TJSONObject;
+ function VisitRecurNode(const Node: IAstNode): TJSONObject;
+ function VisitBlockExpression(const Node: IAstNode): TJSONObject;
+ function VisitVariableDeclaration(const Node: IAstNode): TJSONObject;
+ function VisitAssignment(const Node: IAstNode): TJSONObject;
+ function VisitIndexer(const Node: IAstNode): TJSONObject;
+ function VisitMemberAccess(const Node: IAstNode): TJSONObject;
+ function VisitRecordLiteral(const Node: IAstNode): TJSONObject;
+ function VisitCreateSeries(const Node: IAstNode): TJSONObject;
+ function VisitAddSeriesItem(const Node: IAstNode): TJSONObject;
+ function VisitSeriesLength(const Node: IAstNode): TJSONObject;
+ function VisitNop(const Node: IAstNode): TJSONObject;
+
+ function VisitPipeInput(const Node: IAstNode): TJSONObject;
+ function VisitPipeSelectorList(const Node: IAstNode): TJSONObject;
+ function VisitPipeInputList(const Node: IAstNode): TJSONObject;
+ function VisitPipe(const Node: IAstNode): TJSONObject;
+
protected
- // IAstVisitor implementation for serialization (T = TJSONObject)
- function VisitConstant(const Node: IConstantNode): TJSONObject; override;
- function VisitIdentifier(const Node: IIdentifierNode): TJSONObject; override;
- function VisitKeyword(const Node: IKeywordNode): TJSONObject; override;
+ procedure SetupHandlers; override;
- // List Visitors
- function VisitParameterList(const Node: IParameterList): TJSONObject; override;
- function VisitArgumentList(const Node: IArgumentList): TJSONObject; override;
- function VisitExpressionList(const Node: IExpressionList): TJSONObject; override;
- function VisitRecordFieldList(const Node: IRecordFieldList): TJSONObject; override;
- function VisitRecordField(const Node: IRecordFieldNode): TJSONObject; override;
-
- function VisitIfExpression(const Node: IIfExpressionNode): TJSONObject; override;
- function VisitCondExpression(const Node: ICondExpressionNode): TJSONObject; override;
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): TJSONObject; override;
- function VisitMacroDefinition(const Node: IMacroDefinitionNode): TJSONObject; override;
- function VisitQuasiquote(const Node: IQuasiquoteNode): TJSONObject; override;
- function VisitUnquote(const Node: IUnquoteNode): TJSONObject; override;
- function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TJSONObject; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): TJSONObject; override;
- function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TJSONObject; override;
- function VisitRecurNode(const Node: IRecurNode): TJSONObject; override;
- function VisitBlockExpression(const Node: IBlockExpressionNode): TJSONObject; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TJSONObject; override;
- function VisitAssignment(const Node: IAssignmentNode): TJSONObject; override;
- function VisitIndexer(const Node: IIndexerNode): TJSONObject; override;
- function VisitMemberAccess(const Node: IMemberAccessNode): TJSONObject; override;
- function VisitRecordLiteral(const Node: IRecordLiteralNode): TJSONObject; override;
- function VisitCreateSeries(const Node: ICreateSeriesNode): TJSONObject; override;
- function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TJSONObject; override;
- function VisitSeriesLength(const Node: ISeriesLengthNode): TJSONObject; override;
- function VisitNop(const Node: INopNode): TJSONObject; override;
-
- function Serialize(const RootNode: IAstNode): TJSONObject;
- function Deserialize(const AJson: TJSONObject): IAstNode;
public
constructor Create;
+ function Serialize(const RootNode: IAstNode): TJSONObject;
+ function Deserialize(const AJson: TJSONObject): IAstNode;
end;
implementation
@@ -104,17 +111,64 @@ begin
inherited;
end;
+procedure TJsonAstConverter.SetupHandlers;
+begin
+ Register(akConstant, VisitConstant);
+ Register(akIdentifier, VisitIdentifier);
+ Register(akKeyword, VisitKeyword);
+
+ Register(akParameterList, VisitParameterList);
+ Register(akArgumentList, VisitArgumentList);
+ Register(akExpressionList, VisitExpressionList);
+ Register(akRecordFieldList, VisitRecordFieldList);
+ Register(akRecordField, VisitRecordField);
+
+ Register(akIfExpression, VisitIfExpression);
+ Register(akCondExpression, VisitCondExpression);
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akFunctionCall, VisitFunctionCall);
+ Register(akMacroExpansion, VisitMacroExpansionNode);
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+ Register(akAssignment, VisitAssignment);
+ Register(akMacroDefinition, VisitMacroDefinition);
+ Register(akQuasiquote, VisitQuasiquote);
+ Register(akUnquote, VisitUnquote);
+ Register(akUnquoteSplicing, VisitUnquoteSplicing);
+ Register(akIndexer, VisitIndexer);
+ Register(akMemberAccess, VisitMemberAccess);
+ Register(akRecordLiteral, VisitRecordLiteral);
+ Register(akCreateSeries, VisitCreateSeries);
+ Register(akAddSeriesItem, VisitAddSeriesItem);
+ Register(akSeriesLength, VisitSeriesLength);
+ Register(akRecur, VisitRecurNode);
+ Register(akNop, VisitNop);
+
+ Register(akPipeInput, VisitPipeInput);
+ Register(akPipeSelectorList, VisitPipeSelectorList);
+ Register(akPipeInputList, VisitPipeInputList);
+ Register(akPipe, VisitPipe);
+end;
+
+function TJsonAstConverter.Serialize(const RootNode: IAstNode): TJSONObject;
+begin
+ if not Assigned(RootNode) then
+ begin
+ exit(nil);
+ end;
+ Result := Visit(RootNode);
+end;
+
function TJsonAstConverter.Deserialize(const AJson: TJSONObject): IAstNode;
begin
Result := JsonToNode(AJson);
end;
-{ TJsonAstConverter - IAstVisitor for Serialization }
+{ Serialization Visitors }
procedure TJsonAstConverter.DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string);
var
valObj, scalarObj: TJSONObject;
- kwName: string;
begin
valObj := TJSONObject.Create;
valObj.AddPair('Kind', TJSONString.Create(AValue.Kind.ToString));
@@ -130,16 +184,12 @@ begin
TScalar.TKind.Boolean: scalarObj.AddPair('Value', TJSONBool.Create(AValue.AsScalar.Value.AsInt64 <> 0));
TScalar.TKind.DateTime: scalarObj.AddPair('Value', TJSONNumber.Create(AValue.AsScalar.Value.AsDouble));
TScalar.TKind.Keyword:
- begin
- // Use reverse map to get name from index (AsInt64)
- kwName := TKeywordRegistry.GetName(AValue.AsScalar.Value.AsInt64);
- scalarObj.AddPair('Value', TJSONString.Create(kwName));
- end;
+ scalarObj.AddPair('Value', TJSONString.Create(TKeywordRegistry.GetName(AValue.AsScalar.Value.AsInt64)));
end;
valObj.AddPair('Value', scalarObj);
end;
vkText: valObj.AddPair('Value', TJSONString.Create(AValue.AsText));
- vkVoid:; // No value to add
+ vkVoid:;
else
raise ENotSupportedException.Create('Unsupported TDataValue kind for constant serialization.');
end;
@@ -147,354 +197,380 @@ begin
AParent.AddPair(AName, valObj);
end;
-function TJsonAstConverter.VisitConstant(const Node: IConstantNode): TJSONObject;
+function TJsonAstConverter.VisitConstant(const Node: IAstNode): TJSONObject;
begin
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Constant'));
- DataValueToJson(Node.Value, Result, 'Value');
+ DataValueToJson(Node.AsConstant.Value, Result, 'Value');
end;
-function TJsonAstConverter.VisitIdentifier(const Node: IIdentifierNode): TJSONObject;
+function TJsonAstConverter.VisitIdentifier(const Node: IAstNode): TJSONObject;
begin
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Identifier'));
- Result.AddPair('Name', TJSONString.Create(Node.Name));
+ Result.AddPair('Name', TJSONString.Create(Node.AsIdentifier.Name));
end;
-function TJsonAstConverter.VisitKeyword(const Node: IKeywordNode): TJSONObject;
+function TJsonAstConverter.VisitKeyword(const Node: IAstNode): TJSONObject;
begin
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Keyword'));
- Result.AddPair('Name', TJSONString.Create(Node.Value.Name));
+ Result.AddPair('Name', TJSONString.Create(Node.AsKeyword.Value.Name));
end;
-function TJsonAstConverter.VisitIfExpression(const Node: IIfExpressionNode): TJSONObject;
+function TJsonAstConverter.VisitIfExpression(const Node: IAstNode): TJSONObject;
var
- condObj, thenObj, elseObj: TJSONObject;
+ E: IIfExpressionNode;
+ elseObj: TJSONValue;
begin
- condObj := Accept(Node.Condition);
- thenObj := Accept(Node.ThenBranch);
- elseObj := Accept(Node.ElseBranch); // Accept handles nil
-
+ E := Node.AsIfExpression;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('IfExpr'));
- Result.AddPair('Condition', condObj);
- Result.AddPair('ThenBranch', thenObj);
+ Result.AddPair('Condition', Visit(E.Condition));
+ Result.AddPair('ThenBranch', Visit(E.ThenBranch));
- if Assigned(elseObj) then
- Result.AddPair('ElseBranch', elseObj)
+ if Assigned(E.ElseBranch) then
+ begin
+ elseObj := Visit(E.ElseBranch);
+ end
else
- Result.AddPair('ElseBranch', TJSONNull.Create);
+ begin
+ elseObj := TJSONNull.Create;
+ end;
+ Result.AddPair('ElseBranch', elseObj);
end;
-function TJsonAstConverter.VisitCondExpression(const Node: ICondExpressionNode): TJSONObject;
+function TJsonAstConverter.VisitCondExpression(const Node: IAstNode): TJSONObject;
var
+ E: ICondExpressionNode;
pairsArray: TJSONArray;
pairObj: TJSONObject;
- elseObj: TJSONObject;
+ elseObj: TJSONValue;
i: Integer;
begin
+ E := Node.AsCondExpression;
pairsArray := TJSONArray.Create;
- for i := 0 to High(Node.Pairs) do
+ for i := 0 to High(E.Pairs) do
begin
pairObj := TJSONObject.Create;
- pairObj.AddPair('Condition', Accept(Node.Pairs[i].Condition));
- pairObj.AddPair('Branch', Accept(Node.Pairs[i].Branch));
+ pairObj.AddPair('Condition', Visit(E.Pairs[i].Condition));
+ pairObj.AddPair('Branch', Visit(E.Pairs[i].Branch));
pairsArray.Add(pairObj);
end;
- elseObj := Accept(Node.ElseBranch); // Accept handles nil
+ if Assigned(E.ElseBranch) then
+ begin
+ elseObj := Visit(E.ElseBranch);
+ end
+ else
+ begin
+ elseObj := TJSONNull.Create;
+ end;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('CondExpr'));
Result.AddPair('Pairs', pairsArray);
-
- if Assigned(elseObj) then
- Result.AddPair('ElseBranch', elseObj)
- else
- Result.AddPair('ElseBranch', TJSONNull.Create);
+ Result.AddPair('ElseBranch', elseObj);
end;
-function TJsonAstConverter.VisitLambdaExpression(const Node: ILambdaExpressionNode): TJSONObject;
+function TJsonAstConverter.VisitLambdaExpression(const Node: IAstNode): TJSONObject;
var
- bodyObj: TJSONObject;
+ L: ILambdaExpressionNode;
paramsArray: TJSONArray;
i: Integer;
begin
- bodyObj := Accept(Node.Body);
-
+ L := Node.AsLambdaExpression;
paramsArray := TJSONArray.Create;
- for i := 0 to Node.Parameters.Count - 1 do
- paramsArray.Add(Accept(Node.Parameters[i]));
+ for i := 0 to L.Parameters.Count - 1 do
+ begin
+ paramsArray.Add(Visit(L.Parameters[i]));
+ end;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('LambdaExpr'));
Result.AddPair('Parameters', paramsArray);
- Result.AddPair('Body', bodyObj);
+ Result.AddPair('Body', Visit(L.Body));
end;
-function TJsonAstConverter.VisitMacroDefinition(const Node: IMacroDefinitionNode): TJSONObject;
+function TJsonAstConverter.VisitMacroDefinition(const Node: IAstNode): TJSONObject;
var
- nameObj, bodyObj: TJSONObject;
+ M: IMacroDefinitionNode;
paramsArray: TJSONArray;
i: Integer;
begin
- nameObj := Accept(Node.Name);
- bodyObj := Accept(Node.Body);
-
+ M := Node.AsMacroDefinition;
paramsArray := TJSONArray.Create;
- for i := 0 to Node.Parameters.Count - 1 do
- paramsArray.Add(Accept(Node.Parameters[i]));
+ for i := 0 to M.Parameters.Count - 1 do
+ begin
+ paramsArray.Add(Visit(M.Parameters[i]));
+ end;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('MacroDef'));
- Result.AddPair('Name', nameObj);
+ Result.AddPair('Name', Visit(M.Name));
Result.AddPair('Parameters', paramsArray);
- Result.AddPair('Body', bodyObj);
+ Result.AddPair('Body', Visit(M.Body));
end;
-function TJsonAstConverter.VisitQuasiquote(const Node: IQuasiquoteNode): TJSONObject;
+function TJsonAstConverter.VisitQuasiquote(const Node: IAstNode): TJSONObject;
begin
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Quasiquote'));
- Result.AddPair('Expression', Accept(Node.Expression));
+ Result.AddPair('Expression', Visit(Node.AsQuasiquote.Expression));
end;
-function TJsonAstConverter.VisitUnquote(const Node: IUnquoteNode): TJSONObject;
+function TJsonAstConverter.VisitUnquote(const Node: IAstNode): TJSONObject;
begin
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Unquote'));
- Result.AddPair('Expression', Accept(Node.Expression));
+ Result.AddPair('Expression', Visit(Node.AsUnquote.Expression));
end;
-function TJsonAstConverter.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TJSONObject;
+function TJsonAstConverter.VisitUnquoteSplicing(const Node: IAstNode): TJSONObject;
begin
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('UnquoteSplicing'));
- Result.AddPair('Expression', Accept(Node.Expression));
+ Result.AddPair('Expression', Visit(Node.AsUnquoteSplicing.Expression));
end;
-function TJsonAstConverter.VisitFunctionCall(const Node: IFunctionCallNode): TJSONObject;
+function TJsonAstConverter.VisitFunctionCall(const Node: IAstNode): TJSONObject;
var
- calleeObj: TJSONObject;
+ C: IFunctionCallNode;
argsArray: TJSONArray;
i: Integer;
begin
- calleeObj := Accept(Node.Callee);
-
+ C := Node.AsFunctionCall;
argsArray := TJSONArray.Create;
- for i := 0 to Node.Arguments.Count - 1 do
- argsArray.Add(Accept(Node.Arguments[i]));
+ for i := 0 to C.Arguments.Count - 1 do
+ begin
+ argsArray.Add(Visit(C.Arguments[i]));
+ end;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('FunctionCall'));
- Result.AddPair('Callee', calleeObj);
+ Result.AddPair('Callee', Visit(C.Callee));
Result.AddPair('Arguments', argsArray);
end;
-function TJsonAstConverter.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TJSONObject;
+function TJsonAstConverter.VisitMacroExpansionNode(const Node: IAstNode): TJSONObject;
var
- calleeObj, bodyObj: TJSONObject;
+ M: IMacroExpansionNode;
argsArray: TJSONArray;
i: Integer;
begin
- calleeObj := Accept(Node.CallNode.Callee);
- bodyObj := Accept(Node.ExpandedBody);
-
+ M := Node.AsMacroExpansion;
argsArray := TJSONArray.Create;
- for i := 0 to Node.CallNode.Arguments.Count - 1 do
- argsArray.Add(Accept(Node.CallNode.Arguments[i]));
+ for i := 0 to M.CallNode.Arguments.Count - 1 do
+ begin
+ argsArray.Add(Visit(M.CallNode.Arguments[i]));
+ end;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('MacroExpansion'));
- Result.AddPair('Callee', calleeObj);
+ Result.AddPair('Callee', Visit(M.CallNode.Callee));
Result.AddPair('Arguments', argsArray);
- Result.AddPair('ExpandedBody', bodyObj);
+ Result.AddPair('ExpandedBody', Visit(M.ExpandedBody));
end;
-function TJsonAstConverter.VisitRecurNode(const Node: IRecurNode): TJSONObject;
+function TJsonAstConverter.VisitRecurNode(const Node: IAstNode): TJSONObject;
var
+ R: IRecurNode;
argsArray: TJSONArray;
i: Integer;
begin
+ R := Node.AsRecur;
argsArray := TJSONArray.Create;
- for i := 0 to Node.Arguments.Count - 1 do
- argsArray.Add(Accept(Node.Arguments[i]));
+ for i := 0 to R.Arguments.Count - 1 do
+ begin
+ argsArray.Add(Visit(R.Arguments[i]));
+ end;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Recur'));
Result.AddPair('Arguments', argsArray);
end;
-function TJsonAstConverter.VisitBlockExpression(const Node: IBlockExpressionNode): TJSONObject;
+function TJsonAstConverter.VisitBlockExpression(const Node: IAstNode): TJSONObject;
var
+ B: IBlockExpressionNode;
exprsArray: TJSONArray;
i: Integer;
begin
+ B := Node.AsBlockExpression;
exprsArray := TJSONArray.Create;
- for i := 0 to Node.Expressions.Count - 1 do
- exprsArray.Add(Accept(Node.Expressions[i]));
+ for i := 0 to B.Expressions.Count - 1 do
+ begin
+ exprsArray.Add(Visit(B.Expressions[i]));
+ end;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Block'));
Result.AddPair('Expressions', exprsArray);
end;
-function TJsonAstConverter.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TJSONObject;
+function TJsonAstConverter.VisitVariableDeclaration(const Node: IAstNode): TJSONObject;
var
- identObj, initObj: TJSONObject;
+ V: IVariableDeclarationNode;
+ initObj: TJSONValue;
begin
- identObj := Accept(Node.Target);
- initObj := Accept(Node.Initializer); // Accept handles nil
+ V := Node.AsVariableDeclaration;
+ if Assigned(V.Initializer) then
+ begin
+ initObj := Visit(V.Initializer);
+ end
+ else
+ begin
+ initObj := TJSONNull.Create;
+ end;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('VarDecl'));
- Result.AddPair('Identifier', identObj);
-
- if Assigned(initObj) then
- Result.AddPair('Initializer', initObj)
- else
- Result.AddPair('Initializer', TJSONNull.Create);
+ Result.AddPair('Identifier', Visit(V.Target));
+ Result.AddPair('Initializer', initObj);
end;
-function TJsonAstConverter.VisitAssignment(const Node: IAssignmentNode): TJSONObject;
+function TJsonAstConverter.VisitAssignment(const Node: IAstNode): TJSONObject;
var
- identObj, valueObj: TJSONObject;
+ A: IAssignmentNode;
begin
- identObj := Accept(Node.Target);
- valueObj := Accept(Node.Value);
-
+ A := Node.AsAssignment;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Assignment'));
- Result.AddPair('Identifier', identObj);
- Result.AddPair('Value', valueObj);
+ Result.AddPair('Identifier', Visit(A.Target));
+ Result.AddPair('Value', Visit(A.Value));
end;
-function TJsonAstConverter.VisitIndexer(const Node: IIndexerNode): TJSONObject;
+function TJsonAstConverter.VisitIndexer(const Node: IAstNode): TJSONObject;
var
- baseObj, indexObj: TJSONObject;
+ I: IIndexerNode;
begin
- baseObj := Accept(Node.Base);
- indexObj := Accept(Node.Index);
-
+ I := Node.AsIndexer;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Indexer'));
- Result.AddPair('Base', baseObj);
- Result.AddPair('Index', indexObj);
+ Result.AddPair('Base', Visit(I.Base));
+ Result.AddPair('Index', Visit(I.Index));
end;
-function TJsonAstConverter.VisitMemberAccess(const Node: IMemberAccessNode): TJSONObject;
+function TJsonAstConverter.VisitMemberAccess(const Node: IAstNode): TJSONObject;
var
- baseObj, memberObj: TJSONObject;
+ M: IMemberAccessNode;
begin
- baseObj := Accept(Node.Base);
- memberObj := Accept(Node.Member);
-
+ M := Node.AsMemberAccess;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('MemberAccess'));
- Result.AddPair('Base', baseObj);
- Result.AddPair('Member', memberObj);
+ Result.AddPair('Base', Visit(M.Base));
+ Result.AddPair('Member', Visit(M.Member));
end;
-function TJsonAstConverter.VisitRecordLiteral(const Node: IRecordLiteralNode): TJSONObject;
+function TJsonAstConverter.VisitRecordLiteral(const Node: IAstNode): TJSONObject;
var
+ R: IRecordLiteralNode;
fieldsArray: TJSONArray;
i: Integer;
begin
+ R := Node.AsRecordLiteral;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('RecordLiteral'));
-
fieldsArray := TJSONArray.Create;
- for i := 0 to Node.Fields.Count - 1 do
+ for i := 0 to R.Fields.Count - 1 do
begin
- // We visit the RecordFieldNode, which returns a JSON Object
- fieldsArray.Add(Accept(Node.Fields[i]));
+ fieldsArray.Add(Visit(R.Fields[i]));
end;
-
Result.AddPair('Fields', fieldsArray);
end;
-function TJsonAstConverter.VisitRecordField(const Node: IRecordFieldNode): TJSONObject;
+function TJsonAstConverter.VisitRecordField(const Node: IAstNode): TJSONObject;
+var
+ F: IRecordFieldNode;
begin
+ F := Node.AsRecordField;
Result := TJSONObject.Create;
- Result.AddPair('Name', TJSONString.Create(Node.Key.Value.Name));
- Result.AddPair('Value', Accept(Node.Value));
+ Result.AddPair('Name', TJSONString.Create(F.Key.Value.Name));
+ Result.AddPair('Value', Visit(F.Value));
end;
-function TJsonAstConverter.VisitCreateSeries(const Node: ICreateSeriesNode): TJSONObject;
+function TJsonAstConverter.VisitCreateSeries(const Node: IAstNode): TJSONObject;
begin
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('CreateSeries'));
- Result.AddPair('Definition', TJSONString.Create(Node.Definition));
+ Result.AddPair('Definition', TJSONString.Create(Node.AsCreateSeries.Definition));
end;
-function TJsonAstConverter.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TJSONObject;
+function TJsonAstConverter.VisitAddSeriesItem(const Node: IAstNode): TJSONObject;
var
- seriesObj, valueObj, lookbackObj: TJSONObject;
+ A: IAddSeriesItemNode;
+ lookbackObj: TJSONValue;
begin
- seriesObj := Accept(Node.Series);
- valueObj := Accept(Node.Value);
- lookbackObj := Accept(Node.Lookback); // Accept handles nil
+ A := Node.AsAddSeriesItem;
+ if Assigned(A.Lookback) then
+ begin
+ lookbackObj := Visit(A.Lookback);
+ end
+ else
+ begin
+ lookbackObj := TJSONNull.Create;
+ end;
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('AddSeriesItem'));
- Result.AddPair('Series', seriesObj);
- Result.AddPair('Value', valueObj);
-
- if Assigned(lookbackObj) then
- Result.AddPair('Lookback', lookbackObj)
- else
- Result.AddPair('Lookback', TJSONNull.Create);
+ Result.AddPair('Series', Visit(A.Series));
+ Result.AddPair('Value', Visit(A.Value));
+ Result.AddPair('Lookback', lookbackObj);
end;
-function TJsonAstConverter.VisitSeriesLength(const Node: ISeriesLengthNode): TJSONObject;
-var
- seriesObj: TJSONObject;
+function TJsonAstConverter.VisitSeriesLength(const Node: IAstNode): TJSONObject;
begin
- seriesObj := Accept(Node.Series);
-
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('SeriesLength'));
- Result.AddPair('Series', seriesObj);
+ Result.AddPair('Series', Visit(Node.AsSeriesLength.Series));
end;
-function TJsonAstConverter.VisitNop(const Node: INopNode): TJSONObject;
+function TJsonAstConverter.VisitNop(const Node: IAstNode): TJSONObject;
begin
Result := TJSONObject.Create;
Result.AddPair('NodeType', TJSONString.Create('Nop'));
end;
-// --- List Visitors (Placeholder return) ---
-
-function TJsonAstConverter.VisitParameterList(const Node: IParameterList): TJSONObject;
+function TJsonAstConverter.VisitParameterList(const Node: IAstNode): TJSONObject;
+begin
+ Result := nil;
+end;
+function TJsonAstConverter.VisitArgumentList(const Node: IAstNode): TJSONObject;
+begin
+ Result := nil;
+end;
+function TJsonAstConverter.VisitExpressionList(const Node: IAstNode): TJSONObject;
+begin
+ Result := nil;
+end;
+function TJsonAstConverter.VisitRecordFieldList(const Node: IAstNode): TJSONObject;
begin
Result := nil;
end;
-function TJsonAstConverter.VisitArgumentList(const Node: IArgumentList): TJSONObject;
+function TJsonAstConverter.VisitPipeInput(const Node: IAstNode): TJSONObject;
begin
- Result := nil;
+ raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
+end;
+function TJsonAstConverter.VisitPipeSelectorList(const Node: IAstNode): TJSONObject;
+begin
+ raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
+end;
+function TJsonAstConverter.VisitPipeInputList(const Node: IAstNode): TJSONObject;
+begin
+ raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
+end;
+function TJsonAstConverter.VisitPipe(const Node: IAstNode): TJSONObject;
+begin
+ raise ENotSupportedException.Create('JSON Pipe Serialization not implemented.');
end;
-function TJsonAstConverter.VisitExpressionList(const Node: IExpressionList): TJSONObject;
-begin
- Result := nil;
-end;
-
-function TJsonAstConverter.VisitRecordFieldList(const Node: IRecordFieldList): TJSONObject;
-begin
- Result := nil;
-end;
-
-// ------------------------------------------------------------------------------------------------
-// Deserialization
-// ------------------------------------------------------------------------------------------------
+{ Deserialization Handlers }
function TJsonAstConverter.JsonToDataValue(const AObj: TJSONObject; const AName: string): TDataValue;
var
valObj, scalarObj: TJSONObject;
kindStr: string;
- scalarKind: TScalar.TKind;
begin
valObj := AObj.GetValue(AName);
kindStr := valObj.GetValue('Kind');
@@ -502,16 +578,12 @@ begin
if SameText(kindStr, 'Scalar') then
begin
scalarObj := valObj.GetValue('Value');
- kindStr := scalarObj.GetValue('Kind');
- scalarKind := TScalar.StringToKind(kindStr);
- case scalarKind of
+ case TScalar.StringToKind(scalarObj.GetValue('Kind')) of
TScalar.TKind.Ordinal: Result := TScalar.FromInt64(scalarObj.GetValue('Value').AsInt64);
TScalar.TKind.Float: Result := TScalar.FromDouble(scalarObj.GetValue('Value').AsDouble);
TScalar.TKind.Boolean: Result := TScalar.FromBoolean(scalarObj.GetValue('Value').AsBoolean);
TScalar.TKind.DateTime: Result := TScalar.FromDateTime(scalarObj.GetValue('Value').AsDouble);
- TScalar.TKind.Keyword:
- // Keywords are serialized by name, intern them back
- Result := TScalar.FromKeyword(TKeywordRegistry.Intern(scalarObj.GetValue('Value')));
+ TScalar.TKind.Keyword: Result := TScalar.FromKeyword(TKeywordRegistry.Intern(scalarObj.GetValue('Value')));
end;
end
else if SameText(kindStr, 'Text') then
@@ -523,286 +595,23 @@ begin
Result := TDataValue.Void;
end
else
- raise ENotSupportedException.Create('Unsupported TDataValue kind for constant deserialization.');
-end;
-
-function TJsonAstConverter.JsonToConstantNode(const AObj: TJSONObject): IConstantNode;
-begin
- Result := TAst.Constant(JsonToDataValue(AObj, 'Value'));
-end;
-
-function TJsonAstConverter.JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode;
-begin
- Result := TAst.Identifier(AObj.GetValue('Name'));
-end;
-
-function TJsonAstConverter.JsonToKeywordNode(const AObj: TJSONObject): IKeywordNode;
-begin
- Result := TAst.Keyword(AObj.GetValue('Name'));
-end;
-
-function TJsonAstConverter.JsonToIfExprNode(const AObj: TJSONObject): IIfExpressionNode;
-var
- condNode, thenNode, elseNode: IAstNode;
-begin
- condNode := JsonToNode(AObj.GetValue('Condition'));
- thenNode := JsonToNode(AObj.GetValue('ThenBranch'));
- if not (AObj.GetValue('ElseBranch') is TJSONNull) then
- elseNode := JsonToNode(AObj.GetValue('ElseBranch'))
- else
- elseNode := nil;
- Result := TAst.IfExpr(condNode, thenNode, elseNode);
-end;
-
-function TJsonAstConverter.JsonToCondExprNode(const AObj: TJSONObject): ICondExpressionNode;
-var
- pairsArray: TJSONArray;
- pairs: TArray;
- elseNode: IAstNode;
- i: Integer;
- pairObj: TJSONObject;
-begin
- pairsArray := AObj.GetValue('Pairs');
- SetLength(pairs, pairsArray.Count);
- for i := 0 to pairsArray.Count - 1 do
begin
- pairObj := pairsArray.Items[i] as TJSONObject;
- pairs[i] := TCondPair.Create(JsonToNode(pairObj.GetValue('Condition')), JsonToNode(pairObj.GetValue('Branch')));
+ raise ENotSupportedException.Create('Unsupported TDataValue kind.');
end;
-
- if not (AObj.GetValue('ElseBranch') is TJSONNull) then
- elseNode := JsonToNode(AObj.GetValue('ElseBranch'))
- else
- elseNode := nil;
-
- Result := TAst.CondExpr(pairs, elseNode);
end;
-function TJsonAstConverter.JsonToTernaryExprNode(const AObj: TJSONObject): IAstNode;
-var
- condNode, thenNode, elseNode: IAstNode;
-begin
- condNode := JsonToNode(AObj.GetValue('Condition'));
- thenNode := JsonToNode(AObj.GetValue('ThenBranch'));
- elseNode := JsonToNode(AObj.GetValue('ElseBranch'));
-
- // Map to CondExpr using the helper in TAst
- Result := TAst.TernaryExpr(condNode, thenNode, elseNode);
-end;
-
-function TJsonAstConverter.JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode;
-var
- params: TArray;
- body: IAstNode;
- paramArray: TJSONArray;
- i: Integer;
-begin
- paramArray := AObj.GetValue('Parameters');
- SetLength(params, paramArray.Count);
- for i := 0 to paramArray.Count - 1 do
- params[i] := JsonToIdentifierNode(paramArray.Items[i] as TJSONObject);
-
- body := JsonToNode(AObj.GetValue('Body'));
- Result := TAst.LambdaExpr(params, body);
-end;
-
-function TJsonAstConverter.JsonToMacroDefNode(const AObj: TJSONObject): IMacroDefinitionNode;
-var
- name: IIdentifierNode;
- params: TArray;
- body: IAstNode;
- paramArray: TJSONArray;
- i: Integer;
-begin
- name := JsonToIdentifierNode(AObj.GetValue('Name') as TJSONObject);
-
- paramArray := AObj.GetValue('Parameters');
- SetLength(params, paramArray.Count);
- for i := 0 to paramArray.Count - 1 do
- params[i] := JsonToIdentifierNode(paramArray.Items[i] as TJSONObject);
-
- body := JsonToNode(AObj.GetValue('Body'), 'Quasiquote');
- Result := TAst.MacroDef(name, params, body);
-end;
-
-function TJsonAstConverter.JsonToMacroExpansionNode(const AObj: TJSONObject): IMacroExpansionNode;
-var
- callee, expandedBody: IAstNode;
- args: TArray;
- argsArray: TJSONArray;
- i: Integer;
- tempCallNode: IFunctionCallNode;
-begin
- callee := JsonToNode(AObj.GetValue('Callee'));
- expandedBody := JsonToNode(AObj.GetValue('ExpandedBody'));
- argsArray := AObj.GetValue('Arguments');
- SetLength(args, argsArray.Count);
- for i := 0 to argsArray.Count - 1 do
- args[i] := JsonToNode(argsArray.Items[i]);
-
- tempCallNode := TAst.FunctionCall(callee, args);
- Result := TAst.MacroExpansionNode(tempCallNode, expandedBody);
-end;
-
-function TJsonAstConverter.JsonToQuasiquoteNode(const AObj: TJSONObject): IQuasiquoteNode;
-begin
- Result := TAst.Quasiquote(JsonToNode(AObj.GetValue('Expression')));
-end;
-
-function TJsonAstConverter.JsonToUnquoteNode(const AObj: TJSONObject): IUnquoteNode;
-begin
- Result := TAst.Unquote(JsonToNode(AObj.GetValue('Expression')));
-end;
-
-function TJsonAstConverter.JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode;
-begin
- Result := TAst.UnquoteSplicing(JsonToNode(AObj.GetValue('Expression')).AsQuasiquote);
-end;
-
-function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode;
-var
- callee: IAstNode;
- args: TArray;
- argsArray: TJSONArray;
- i: Integer;
-begin
- callee := JsonToNode(AObj.GetValue('Callee'));
- argsArray := AObj.GetValue('Arguments');
- SetLength(args, argsArray.Count);
- for i := 0 to argsArray.Count - 1 do
- args[i] := JsonToNode(argsArray.Items[i]);
-
- Result := TAst.FunctionCall(callee, args);
-end;
-
-function TJsonAstConverter.JsonToRecurNode(const AObj: TJSONObject): IRecurNode;
-var
- args: TArray;
- argsArray: TJSONArray;
- i: Integer;
-begin
- argsArray := AObj.GetValue('Arguments');
- SetLength(args, argsArray.Count);
- for i := 0 to argsArray.Count - 1 do
- args[i] := JsonToNode(argsArray.Items[i]);
-
- Result := TAst.Recur(args);
-end;
-
-function TJsonAstConverter.JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode;
-var
- expressions: TArray;
- exprsArray: TJSONArray;
- i: Integer;
-begin
- exprsArray := AObj.GetValue('Expressions');
- SetLength(expressions, exprsArray.Count);
- for i := 0 to exprsArray.Count - 1 do
- expressions[i] := JsonToNode(exprsArray.Items[i]);
-
- Result := TAst.Block(expressions);
-end;
-
-function TJsonAstConverter.JsonToVarDeclNode(const AObj: TJSONObject): IVariableDeclarationNode;
-var
- ident: IIdentifierNode;
- initializer: IAstNode;
-begin
- ident := JsonToIdentifierNode(AObj.GetValue('Identifier') as TJSONObject);
- if not (AObj.GetValue('Initializer') is TJSONNull) then
- initializer := JsonToNode(AObj.GetValue('Initializer'))
- else
- initializer := nil;
-
- Result := TAst.VarDecl(ident, initializer);
-end;
-
-function TJsonAstConverter.JsonToAssignmentNode(const AObj: TJSONObject): IAssignmentNode;
-var
- ident: IIdentifierNode;
- value: IAstNode;
-begin
- ident := JsonToIdentifierNode(AObj.GetValue('Identifier') as TJSONObject);
- value := JsonToNode(AObj.GetValue('Value'));
- Result := TAst.Assign(ident, value);
-end;
-
-function TJsonAstConverter.JsonToIndexerNode(const AObj: TJSONObject): IIndexerNode;
-begin
- Result := TAst.Indexer(JsonToNode(AObj.GetValue('Base')), JsonToNode(AObj.GetValue('Index')));
-end;
-
-function TJsonAstConverter.JsonToMemberAccessNode(const AObj: TJSONObject): IMemberAccessNode;
-begin
- Result := TAst.MemberAccess(JsonToNode(AObj.GetValue('Base')), JsonToKeywordNode(AObj.GetValue('Member') as TJSONObject));
-end;
-
-function TJsonAstConverter.JsonToRecordLiteralNode(const AObj: TJSONObject): IRecordLiteralNode;
-var
- fields: TArray;
- fieldsArray: TJSONArray;
- fieldObj: TJSONObject;
- i: Integer;
- keyNode: IKeywordNode;
- valueNode: IAstNode;
-begin
- fieldsArray := AObj.GetValue('Fields');
- SetLength(fields, fieldsArray.Count);
-
- for i := 0 to fieldsArray.Count - 1 do
- begin
- fieldObj := fieldsArray.Items[i] as TJSONObject;
- keyNode := TAst.Keyword(fieldObj.GetValue('Name'));
- valueNode := JsonToNode(fieldObj.GetValue('Value'));
-
- fields[i] := TAst.RecordField(keyNode, valueNode);
- end;
-
- Result := TAst.RecordLiteral(fields);
-end;
-
-function TJsonAstConverter.JsonToCreateSeriesNode(const AObj: TJSONObject): ICreateSeriesNode;
-begin
- Result := TAst.CreateSeries(AObj.GetValue('Definition'));
-end;
-
-function TJsonAstConverter.JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode;
-var
- lookbackNode: IAstNode;
-begin
- if not (AObj.GetValue('Lookback') is TJSONNull) then
- lookbackNode := JsonToNode(AObj.GetValue('Lookback'))
- else
- lookbackNode := nil;
-
- Result :=
- TAst.AddSeriesItem(JsonToIdentifierNode(AObj.GetValue('Series') as TJSONObject), JsonToNode(AObj.GetValue('Value')), lookbackNode);
-end;
-
-function TJsonAstConverter.JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode;
-begin
- Result := TAst.SeriesLength(JsonToIdentifierNode(AObj.GetValue('Series') as TJSONObject));
-end;
-
-function TJsonAstConverter.JsonToNopNode(const AObj: TJSONObject): INopNode;
-begin
- Result := TAst.Nop.AsNop;
-end;
-
-function TJsonAstConverter.JsonToNode(const AJson: TJSONValue; const ExpectedType: String = ''): IAstNode;
+function TJsonAstConverter.JsonToNode(const AJson: TJSONValue; const ExpectedType: string): IAstNode;
var
obj: TJSONObject;
nodeType: string;
begin
if not (AJson is TJSONObject) then
- raise EInvalidCast.Create('Expected a JSON object for node deserialization.');
-
+ begin
+ raise EInvalidCast.Create('Expected JSON object.');
+ end;
obj := AJson as TJSONObject;
nodeType := obj.GetValue('NodeType');
- if (ExpectedType <> '') and (nodeType <> ExpectedType) then
- raise EInvalidCast.CreateFmt('Expected a JSON object of type %s for deserialization, but got a %s.', [ExpectedType, nodeType]);
-
if nodeType = 'Constant' then
Result := JsonToConstantNode(obj)
else if nodeType = 'Identifier' then
@@ -814,7 +623,7 @@ begin
else if nodeType = 'CondExpr' then
Result := JsonToCondExprNode(obj)
else if nodeType = 'TernaryExpr' then
- Result := JsonToTernaryExprNode(obj) // Backward Compatibility
+ Result := JsonToTernaryExprNode(obj)
else if nodeType = 'LambdaExpr' then
Result := JsonToLambdaExprNode(obj)
else if nodeType = 'MacroDef' then
@@ -852,17 +661,271 @@ begin
else if nodeType = 'Nop' then
Result := JsonToNopNode(obj)
else
- raise ENotSupportedException.CreateFmt('Unsupported NodeType "%s" for JSON deserialization.', [nodeType]);
+ raise ENotSupportedException.CreateFmt('Unsupported NodeType "%s".', [nodeType]);
end;
-function TJsonAstConverter.Serialize(const RootNode: IAstNode): TJSONObject;
+function TJsonAstConverter.JsonToConstantNode(const AObj: TJSONObject): IConstantNode;
begin
- if not Assigned(RootNode) then
- exit(nil);
+ Result := TAst.Constant(JsonToDataValue(AObj, 'Value'));
+end;
- // Call Accept, which returns TDataValue(vkGeneric(TJSONObject))
- // and unwrap it.
- Result := RootNode.Accept(Self).AsGeneric;
+function TJsonAstConverter.JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode;
+begin
+ Result := TAst.Identifier(AObj.GetValue('Name'));
+end;
+
+function TJsonAstConverter.JsonToKeywordNode(const AObj: TJSONObject): IKeywordNode;
+begin
+ Result := TAst.Keyword(AObj.GetValue('Name'));
+end;
+
+function TJsonAstConverter.JsonToIfExprNode(const AObj: TJSONObject): IIfExpressionNode;
+var
+ elseNode: IAstNode;
+begin
+ if AObj.GetValue('ElseBranch') is TJSONNull then
+ begin
+ elseNode := nil;
+ end
+ else
+ begin
+ elseNode := JsonToNode(AObj.GetValue('ElseBranch'));
+ end;
+
+ Result := TAst.IfExpr(JsonToNode(AObj.GetValue('Condition')), JsonToNode(AObj.GetValue('ThenBranch')), elseNode);
+end;
+
+function TJsonAstConverter.JsonToCondExprNode(const AObj: TJSONObject): ICondExpressionNode;
+var
+ pairsArray: TJSONArray;
+ pairs: TArray;
+ i: Integer;
+ elseNode: IAstNode;
+begin
+ pairsArray := AObj.GetValue('Pairs');
+ SetLength(pairs, pairsArray.Count);
+ for i := 0 to pairsArray.Count - 1 do
+ begin
+ pairs[i] :=
+ TCondPair.Create(
+ JsonToNode((pairsArray.Items[i] as TJSONObject).GetValue('Condition')),
+ JsonToNode((pairsArray.Items[i] as TJSONObject).GetValue('Branch'))
+ );
+ end;
+
+ if AObj.GetValue('ElseBranch') is TJSONNull then
+ begin
+ elseNode := nil;
+ end
+ else
+ begin
+ elseNode := JsonToNode(AObj.GetValue('ElseBranch'));
+ end;
+
+ Result := TAst.CondExpr(pairs, elseNode);
+end;
+
+function TJsonAstConverter.JsonToTernaryExprNode(const AObj: TJSONObject): IAstNode;
+begin
+ Result :=
+ TAst.TernaryExpr(
+ JsonToNode(AObj.GetValue('Condition')),
+ JsonToNode(AObj.GetValue('ThenBranch')),
+ JsonToNode(AObj.GetValue('ElseBranch'))
+ );
+end;
+
+function TJsonAstConverter.JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode;
+var
+ params: TArray;
+ paramArray: TJSONArray;
+ i: Integer;
+begin
+ paramArray := AObj.GetValue('Parameters');
+ SetLength(params, paramArray.Count);
+ for i := 0 to paramArray.Count - 1 do
+ begin
+ params[i] := JsonToIdentifierNode(paramArray.Items[i] as TJSONObject);
+ end;
+ Result := TAst.LambdaExpr(params, JsonToNode(AObj.GetValue('Body')));
+end;
+
+function TJsonAstConverter.JsonToMacroDefNode(const AObj: TJSONObject): IMacroDefinitionNode;
+var
+ params: TArray;
+ paramArray: TJSONArray;
+ i: Integer;
+begin
+ paramArray := AObj.GetValue('Parameters');
+ SetLength(params, paramArray.Count);
+ for i := 0 to paramArray.Count - 1 do
+ begin
+ params[i] := JsonToIdentifierNode(paramArray.Items[i] as TJSONObject);
+ end;
+ Result := TAst.MacroDef(JsonToIdentifierNode(AObj.GetValue('Name') as TJSONObject), params, JsonToNode(AObj.GetValue('Body')));
+end;
+
+function TJsonAstConverter.JsonToQuasiquoteNode(const AObj: TJSONObject): IQuasiquoteNode;
+begin
+ Result := TAst.Quasiquote(JsonToNode(AObj.GetValue('Expression')));
+end;
+
+function TJsonAstConverter.JsonToUnquoteNode(const AObj: TJSONObject): IUnquoteNode;
+begin
+ Result := TAst.Unquote(JsonToNode(AObj.GetValue('Expression')));
+end;
+
+function TJsonAstConverter.JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode;
+begin
+ Result := TAst.UnquoteSplicing(JsonToNode(AObj.GetValue('Expression')).AsQuasiquote);
+end;
+
+function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode;
+var
+ args: TArray;
+ argsArray: TJSONArray;
+ i: Integer;
+begin
+ argsArray := AObj.GetValue('Arguments');
+ SetLength(args, argsArray.Count);
+ for i := 0 to argsArray.Count - 1 do
+ begin
+ args[i] := JsonToNode(argsArray.Items[i]);
+ end;
+ Result := TAst.FunctionCall(JsonToNode(AObj.GetValue('Callee')), args);
+end;
+
+function TJsonAstConverter.JsonToMacroExpansionNode(const AObj: TJSONObject): IMacroExpansionNode;
+var
+ callee, expandedBody: IAstNode;
+ args: TArray;
+ argsArray: TJSONArray;
+ i: Integer;
+ tempCallNode: IFunctionCallNode;
+begin
+ callee := JsonToNode(AObj.GetValue('Callee'));
+ expandedBody := JsonToNode(AObj.GetValue('ExpandedBody'));
+ argsArray := AObj.GetValue('Arguments');
+ SetLength(args, argsArray.Count);
+ for i := 0 to argsArray.Count - 1 do
+ begin
+ args[i] := JsonToNode(argsArray.Items[i]);
+ end;
+
+ tempCallNode := TAst.FunctionCall(callee, args);
+ Result := TAst.MacroExpansionNode(tempCallNode, expandedBody);
+end;
+
+function TJsonAstConverter.JsonToRecurNode(const AObj: TJSONObject): IRecurNode;
+var
+ args: TArray;
+ argsArray: TJSONArray;
+ i: Integer;
+begin
+ argsArray := AObj.GetValue('Arguments');
+ SetLength(args, argsArray.Count);
+ for i := 0 to argsArray.Count - 1 do
+ begin
+ args[i] := JsonToNode(argsArray.Items[i]);
+ end;
+ Result := TAst.Recur(args);
+end;
+
+function TJsonAstConverter.JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode;
+var
+ exprs: TArray;
+ exprsArray: TJSONArray;
+ i: Integer;
+begin
+ exprsArray := AObj.GetValue('Expressions');
+ SetLength(exprs, exprsArray.Count);
+ for i := 0 to exprsArray.Count - 1 do
+ begin
+ exprs[i] := JsonToNode(exprsArray.Items[i]);
+ end;
+ Result := TAst.Block(exprs);
+end;
+
+function TJsonAstConverter.JsonToVarDeclNode(const AObj: TJSONObject): IVariableDeclarationNode;
+var
+ initNode: IAstNode;
+begin
+ if AObj.GetValue('Initializer') is TJSONNull then
+ begin
+ initNode := nil;
+ end
+ else
+ begin
+ initNode := JsonToNode(AObj.GetValue('Initializer'));
+ end;
+
+ Result := TAst.VarDecl(JsonToIdentifierNode(AObj.GetValue('Identifier') as TJSONObject), initNode);
+end;
+
+function TJsonAstConverter.JsonToAssignmentNode(const AObj: TJSONObject): IAssignmentNode;
+begin
+ Result := TAst.Assign(JsonToIdentifierNode(AObj.GetValue('Identifier') as TJSONObject), JsonToNode(AObj.GetValue('Value')));
+end;
+
+function TJsonAstConverter.JsonToIndexerNode(const AObj: TJSONObject): IIndexerNode;
+begin
+ Result := TAst.Indexer(JsonToNode(AObj.GetValue('Base')), JsonToNode(AObj.GetValue('Index')));
+end;
+
+function TJsonAstConverter.JsonToMemberAccessNode(const AObj: TJSONObject): IMemberAccessNode;
+begin
+ Result := TAst.MemberAccess(JsonToNode(AObj.GetValue('Base')), JsonToKeywordNode(AObj.GetValue('Member') as TJSONObject));
+end;
+
+function TJsonAstConverter.JsonToRecordLiteralNode(const AObj: TJSONObject): IRecordLiteralNode;
+var
+ fields: TArray;
+ fieldsArray: TJSONArray;
+ i: Integer;
+begin
+ fieldsArray := AObj.GetValue('Fields');
+ SetLength(fields, fieldsArray.Count);
+ for i := 0 to fieldsArray.Count - 1 do
+ begin
+ fields[i] :=
+ TAst.RecordField(
+ TAst.Keyword((fieldsArray.Items[i] as TJSONObject).GetValue('Name')),
+ JsonToNode((fieldsArray.Items[i] as TJSONObject).GetValue('Value'))
+ );
+ end;
+ Result := TAst.RecordLiteral(fields);
+end;
+
+function TJsonAstConverter.JsonToCreateSeriesNode(const AObj: TJSONObject): ICreateSeriesNode;
+begin
+ Result := TAst.CreateSeries(AObj.GetValue('Definition'));
+end;
+
+function TJsonAstConverter.JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode;
+var
+ lookNode: IAstNode;
+begin
+ if AObj.GetValue('Lookback') is TJSONNull then
+ begin
+ lookNode := nil;
+ end
+ else
+ begin
+ lookNode := JsonToNode(AObj.GetValue('Lookback'));
+ end;
+
+ Result :=
+ TAst.AddSeriesItem(JsonToIdentifierNode(AObj.GetValue('Series') as TJSONObject), JsonToNode(AObj.GetValue('Value')), lookNode);
+end;
+
+function TJsonAstConverter.JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode;
+begin
+ Result := TAst.SeriesLength(JsonToIdentifierNode(AObj.GetValue('Series') as TJSONObject));
+end;
+
+function TJsonAstConverter.JsonToNopNode(const AObj: TJSONObject): INopNode;
+begin
+ Result := TAst.Nop.AsNop;
end;
end.
diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas
index 4e824db..accfd59 100644
--- a/Src/AST/Myc.Ast.Nodes.pas
+++ b/Src/AST/Myc.Ast.Nodes.pas
@@ -1715,6 +1715,10 @@ constructor TLambdaExpressionNode.Create(
);
begin
inherited Create(AStaticType, AIdentity);
+
+ Assert(Assigned(AParameters));
+ Assert(Assigned(ABody));
+
FParameters := AParameters;
FBody := ABody;
FLayout := ALayout;
diff --git a/Src/AST/Myc.Ast.Refactoring.Remove.pas b/Src/AST/Myc.Ast.Refactoring.Remove.pas
index daa6b85..8a6c2a5 100644
--- a/Src/AST/Myc.Ast.Refactoring.Remove.pas
+++ b/Src/AST/Myc.Ast.Refactoring.Remove.pas
@@ -13,47 +13,39 @@ uses
type
// Removes a specific node from the AST.
- // - If the node is part of a list, it is removed from the list.
- // - If the node is a mandatory child of a structure (e.g. If-Condition),
- // it is replaced by a Nop node to maintain tree integrity.
TAstNodeRemover = class(TAstTransformer)
private
FTarget: IAstNode;
FRemoved: Boolean;
- // Generic helper to filter nil items from lists during transformation
function FilterList(const Source: INodeList): TArray;
-
- // Helper to ensure a mandatory slot is never nil (replaces with Nop)
function EnsureNode(const Node: IAstNode; const ContextNode: IAstNode): IAstNode;
+
+ strict private
+ // List Containers (Filter Logic)
+ function VisitBlockExpression(const Node: IAstNode): IAstNode;
+ function VisitExpressionList(const Node: IAstNode): IAstNode;
+ function VisitArgumentList(const Node: IAstNode): IAstNode;
+ function VisitParameterList(const Node: IAstNode): IAstNode;
+ function VisitRecordFieldList(const Node: IAstNode): IAstNode;
+
+ // Structural Nodes (Replace Logic)
+ function VisitIfExpression(const Node: IAstNode): IAstNode;
+ function VisitCondExpression(const Node: IAstNode): IAstNode;
+ function VisitAssignment(const Node: IAstNode): IAstNode;
+ function VisitVariableDeclaration(const Node: IAstNode): IAstNode;
+ function VisitLambdaExpression(const Node: IAstNode): IAstNode;
+ function VisitFunctionCall(const Node: IAstNode): IAstNode;
+ function VisitIndexer(const Node: IAstNode): IAstNode;
+ function VisitMemberAccess(const Node: IAstNode): IAstNode;
+ function VisitAddSeriesItem(const Node: IAstNode): IAstNode;
+
protected
+ procedure SetupHandlers; override;
function Accept(const Node: IAstNode): IAstNode; override;
- // --- List Containers (Filter Logic) ---
- function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
- function VisitExpressionList(const Node: IExpressionList): IAstNode; override;
- function VisitArgumentList(const Node: IArgumentList): IAstNode; override;
- function VisitParameterList(const Node: IParameterList): IAstNode; override;
- function VisitRecordFieldList(const Node: IRecordFieldList): IAstNode; override;
-
- // --- Structural Nodes (Replace Logic) ---
- function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
- function VisitCondExpression(const Node: ICondExpressionNode): IAstNode; override; // Replaces Ternary
- function VisitAssignment(const Node: IAssignmentNode): IAstNode; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
- function VisitIndexer(const Node: IIndexerNode): IAstNode; override;
- function VisitMemberAccess(const Node: IMemberAccessNode): IAstNode; override;
- function VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode; override;
-
public
constructor Create(const ATarget: IAstNode);
-
- // Tries to remove TargetNode from RootNode.
- // Returns True if the node was found and removed/replaced.
- // If successful, NewRootNode contains the modified AST.
- // If not found, NewRootNode returns the original RootNode.
class function TryRemove(const RootNode, TargetNode: IAstNode; out NewRootNode: IAstNode): Boolean; static;
end;
@@ -68,14 +60,35 @@ begin
FRemoved := False;
end;
+procedure TAstNodeRemover.SetupHandlers;
+begin
+ inherited SetupHandlers; // Defaults
+
+ // Filter Logic
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akExpressionList, VisitExpressionList);
+ Register(akArgumentList, VisitArgumentList);
+ Register(akParameterList, VisitParameterList);
+ Register(akRecordFieldList, VisitRecordFieldList);
+
+ // Replace Logic
+ Register(akIfExpression, VisitIfExpression);
+ Register(akCondExpression, VisitCondExpression);
+ Register(akAssignment, VisitAssignment);
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akFunctionCall, VisitFunctionCall);
+ Register(akIndexer, VisitIndexer);
+ Register(akMemberAccess, VisitMemberAccess);
+ Register(akAddSeriesItem, VisitAddSeriesItem);
+end;
+
class function TAstNodeRemover.TryRemove(const RootNode, TargetNode: IAstNode; out NewRootNode: IAstNode): Boolean;
var
Remover: TAstNodeRemover;
begin
- // Edge case: Removing the root itself
if (RootNode = TargetNode) then
begin
- // We replace the root with a Nop, as we cannot return 'nil' for a valid AST root expectation usually
NewRootNode := TAst.Nop(RootNode.Identity);
Result := True;
Exit;
@@ -87,15 +100,9 @@ begin
Result := Remover.FRemoved;
if not Result then
- begin
- // If nothing was removed, return the original node to ensure reference stability
- NewRootNode := RootNode;
- end
+ NewRootNode := RootNode
else if (NewRootNode = nil) then
- begin
- // Safety fallback: if traversal somehow resulted in nil (should be caught by EnsureNode logic usually)
NewRootNode := TAst.Nop(RootNode.Identity);
- end;
finally
Remover.Free;
end;
@@ -103,14 +110,11 @@ end;
function TAstNodeRemover.Accept(const Node: IAstNode): IAstNode;
begin
- // If we matched the target, flag it as removed and return nil.
- // The parent visitor is responsible for handling the nil result (Filter or Replace).
if Node = FTarget then
begin
FRemoved := True;
Exit(nil);
end;
-
Result := inherited Accept(Node);
end;
@@ -119,7 +123,6 @@ begin
if Assigned(Node) then
Result := Node
else
- // Mandatory child was deleted -> Replace with Nop using parent's identity location
Result := TAst.Nop(ContextNode.Identity);
end;
@@ -136,9 +139,6 @@ begin
begin
item := Source[i];
transformed := Accept(item);
-
- // If transformed is not nil, keep it.
- // If it is nil, it was the target, so we skip (delete) it.
if Assigned(transformed) then
list.Add(T(transformed));
end;
@@ -152,107 +152,114 @@ end;
// List Visitors (Filtering)
// -----------------------------------------------------------------------------
-function TAstNodeRemover.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
+function TAstNodeRemover.VisitBlockExpression(const Node: IAstNode): IAstNode;
var
+ B: IBlockExpressionNode;
newExprs: TArray;
newList: IExpressionList;
+ changed: Boolean;
+ i: Integer;
begin
- newExprs := FilterList(Node.Expressions);
+ B := Node.AsBlockExpression;
+ newExprs := FilterList(B.Expressions);
- // If counts differ, something was removed
- if Length(newExprs) = Node.Expressions.Count then
+ if Length(newExprs) = B.Expressions.Count then
begin
- // If not marked as removed, we assume no change in this branch
if not FRemoved then
Exit(Node);
-
- // Deep check for replacement
- var changed := False;
- for var i := 0 to High(newExprs) do
- if newExprs[i] <> Node.Expressions[i] then
+ changed := False;
+ for i := 0 to High(newExprs) do
+ if newExprs[i] <> B.Expressions[i] then
changed := True;
-
if not changed then
Exit(Node);
end;
- newList := TExpressionList.Create(newExprs, Node.Expressions.Identity);
- Result := TAst.Block(Node.Identity, newList, Node.StaticType);
+ newList := TExpressionList.Create(newExprs, B.Expressions.Identity);
+ Result := TAst.Block(Node.Identity, newList, B.StaticType);
end;
-function TAstNodeRemover.VisitExpressionList(const Node: IExpressionList): IAstNode;
+function TAstNodeRemover.VisitExpressionList(const Node: IAstNode): IAstNode;
var
+ L: IExpressionList;
newExprs: TArray;
+ changed: Boolean;
+ i: Integer;
begin
- newExprs := FilterList(Node);
-
- if Length(newExprs) = Node.Count then
+ L := Node.AsExpressionList;
+ newExprs := FilterList(L);
+ if Length(newExprs) = L.Count then
begin
- var changed := False;
- for var i := 0 to High(newExprs) do
- if newExprs[i] <> Node[i] then
+ changed := False;
+ for i := 0 to High(newExprs) do
+ if newExprs[i] <> L[i] then
changed := True;
if not changed then
Exit(Node);
end;
-
Result := TExpressionList.Create(newExprs, Node.Identity);
end;
-function TAstNodeRemover.VisitArgumentList(const Node: IArgumentList): IAstNode;
+function TAstNodeRemover.VisitArgumentList(const Node: IAstNode): IAstNode;
var
+ L: IArgumentList;
newArgs: TArray;
+ changed: Boolean;
+ i: Integer;
begin
- newArgs := FilterList(Node);
-
- if Length(newArgs) = Node.Count then
+ L := Node.AsArgumentList;
+ newArgs := FilterList(L);
+ if Length(newArgs) = L.Count then
begin
- var changed := False;
- for var i := 0 to High(newArgs) do
- if newArgs[i] <> Node[i] then
+ changed := False;
+ for i := 0 to High(newArgs) do
+ if newArgs[i] <> L[i] then
changed := True;
if not changed then
Exit(Node);
end;
-
Result := TArgumentList.Create(newArgs, Node.Identity);
end;
-function TAstNodeRemover.VisitParameterList(const Node: IParameterList): IAstNode;
+function TAstNodeRemover.VisitParameterList(const Node: IAstNode): IAstNode;
var
+ L: IParameterList;
newParams: TArray;
+ changed: Boolean;
+ i: Integer;
begin
- newParams := FilterList(Node);
-
- if Length(newParams) = Node.Count then
+ L := Node.AsParameterList;
+ newParams := FilterList(L);
+ if Length(newParams) = L.Count then
begin
- var changed := False;
- for var i := 0 to High(newParams) do
- if newParams[i] <> Node[i] then
+ changed := False;
+ for i := 0 to High(newParams) do
+ if newParams[i] <> L[i] then
changed := True;
if not changed then
Exit(Node);
end;
-
Result := TParameterList.Create(newParams, Node.Identity);
end;
-function TAstNodeRemover.VisitRecordFieldList(const Node: IRecordFieldList): IAstNode;
+function TAstNodeRemover.VisitRecordFieldList(const Node: IAstNode): IAstNode;
var
+ L: IRecordFieldList;
newFields: TArray;
+ changed: Boolean;
+ i: Integer;
begin
- newFields := FilterList(Node);
-
- if Length(newFields) = Node.Count then
+ L := Node.AsRecordFieldList;
+ newFields := FilterList(L);
+ if Length(newFields) = L.Count then
begin
- var changed := False;
- for var i := 0 to High(newFields) do
- if newFields[i] <> Node[i] then
+ changed := False;
+ for i := 0 to High(newFields) do
+ if newFields[i] <> L[i] then
changed := True;
if not changed then
Exit(Node);
end;
-
Result := TRecordFieldList.Create(newFields, Node.Identity);
end;
@@ -260,190 +267,166 @@ end;
// Structural Visitors (Replacement with Nop/Void)
// -----------------------------------------------------------------------------
-function TAstNodeRemover.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
+function TAstNodeRemover.VisitIfExpression(const Node: IAstNode): IAstNode;
var
+ E: IIfExpressionNode;
newCond, newThen, newElse: IAstNode;
begin
- newCond := Accept(Node.Condition);
- newThen := Accept(Node.ThenBranch);
- newElse := Accept(Node.ElseBranch);
+ E := Node.AsIfExpression;
+ newCond := EnsureNode(Accept(E.Condition), Node);
+ newThen := EnsureNode(Accept(E.ThenBranch), Node);
+ newElse := Accept(E.ElseBranch); // Optional
- // If condition or Then-branch is deleted, replace with Nop
- newCond := EnsureNode(newCond, Node);
- newThen := EnsureNode(newThen, Node);
-
- // Else branch is optional
- if (newCond = Node.Condition) and (newThen = Node.ThenBranch) and (newElse = Node.ElseBranch) then
+ if (newCond = E.Condition) and (newThen = E.ThenBranch) and (newElse = E.ElseBranch) then
Exit(Node);
-
- Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, Node.StaticType);
+ Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, E.StaticType);
end;
-function TAstNodeRemover.VisitCondExpression(const Node: ICondExpressionNode): IAstNode;
+function TAstNodeRemover.VisitCondExpression(const Node: IAstNode): IAstNode;
var
+ E: ICondExpressionNode;
i: Integer;
newPairs: TArray;
newElse: IAstNode;
newCond, newBranch: IAstNode;
hasChanged: Boolean;
begin
- SetLength(newPairs, Length(Node.Pairs));
+ E := Node.AsCondExpression;
+ SetLength(newPairs, Length(E.Pairs));
hasChanged := False;
- for i := 0 to High(Node.Pairs) do
+ for i := 0 to High(E.Pairs) do
begin
- // Ensure mandatory children exist (replace with Nop if removed)
- newCond := EnsureNode(Accept(Node.Pairs[i].Condition), Node);
- newBranch := EnsureNode(Accept(Node.Pairs[i].Branch), Node);
-
+ newCond := EnsureNode(Accept(E.Pairs[i].Condition), Node);
+ newBranch := EnsureNode(Accept(E.Pairs[i].Branch), Node);
newPairs[i] := TCondPair.Create(newCond, newBranch);
-
- if (newCond <> Node.Pairs[i].Condition) or (newBranch <> Node.Pairs[i].Branch) then
+ if (newCond <> E.Pairs[i].Condition) or (newBranch <> E.Pairs[i].Branch) then
hasChanged := True;
end;
- // Else branch is effectively mandatory in the structure, even if it's Nop/Void
- newElse := EnsureNode(Accept(Node.ElseBranch), Node);
- if newElse <> Node.ElseBranch then
+ newElse := EnsureNode(Accept(E.ElseBranch), Node);
+ if newElse <> E.ElseBranch then
hasChanged := True;
if not hasChanged then
Exit(Node);
-
- Result := TAst.CondExpr(Node.Identity, newPairs, newElse, Node.StaticType);
+ Result := TAst.CondExpr(Node.Identity, newPairs, newElse, E.StaticType);
end;
-function TAstNodeRemover.VisitAssignment(const Node: IAssignmentNode): IAstNode;
+function TAstNodeRemover.VisitAssignment(const Node: IAstNode): IAstNode;
var
+ A: IAssignmentNode;
newTarget, newValue: IAstNode;
begin
- newTarget := EnsureNode(Accept(Node.Target), Node);
- newValue := EnsureNode(Accept(Node.Value), Node);
-
- if (newTarget = Node.Target) and (newValue = Node.Value) then
+ A := Node.AsAssignment;
+ newTarget := EnsureNode(Accept(A.Target), Node);
+ newValue := EnsureNode(Accept(A.Value), Node);
+ if (newTarget = A.Target) and (newValue = A.Value) then
Exit(Node);
-
- Result := TAst.Assign(Node.Identity, newTarget, newValue, Node.StaticType);
+ Result := TAst.Assign(Node.Identity, newTarget, newValue, A.StaticType);
end;
-function TAstNodeRemover.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
+function TAstNodeRemover.VisitVariableDeclaration(const Node: IAstNode): IAstNode;
var
+ V: IVariableDeclarationNode;
newTarget, newInit: IAstNode;
begin
- newTarget := EnsureNode(Accept(Node.Target), Node);
- newInit := Accept(Node.Initializer); // Initializer is optional
-
- if (newTarget = Node.Target) and (newInit = Node.Initializer) then
+ V := Node.AsVariableDeclaration;
+ newTarget := EnsureNode(Accept(V.Target), Node);
+ newInit := Accept(V.Initializer);
+ if (newTarget = V.Target) and (newInit = V.Initializer) then
Exit(Node);
-
- Result := TAst.VarDecl(Node.Identity, newTarget, newInit, Node.StaticType, Node.IsBoxed);
+ Result := TAst.VarDecl(Node.Identity, newTarget, newInit, V.StaticType, V.IsBoxed);
end;
-function TAstNodeRemover.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
+function TAstNodeRemover.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
- newParams: IAstNode;
- newBody: IAstNode;
+ L: ILambdaExpressionNode;
+ newParams, newBody: IAstNode;
begin
- newParams := Accept(Node.Parameters);
- // If the parameter list itself was the target, we replace it with an empty list
+ L := Node.AsLambdaExpression;
+ newParams := Accept(L.Parameters);
if newParams = nil then
- newParams := TParameterList.Create([], Node.Parameters.Identity);
+ newParams := TParameterList.Create([], L.Parameters.Identity);
+ newBody := EnsureNode(Accept(L.Body), Node);
- newBody := EnsureNode(Accept(Node.Body), Node);
-
- if (newParams = Node.Parameters) and (newBody = Node.Body) then
+ if (newParams = L.Parameters) and (newBody = L.Body) then
Exit(Node);
-
Result :=
TAst.LambdaExpr(
Node.Identity,
newParams.AsParameterList,
newBody,
- Node.Layout,
- Node.Descriptor,
- Node.Upvalues,
- Node.HasNestedLambdas,
- Node.IsPure,
- Node.StaticType
+ L.Layout,
+ L.Descriptor,
+ L.Upvalues,
+ L.HasNestedLambdas,
+ L.IsPure,
+ L.StaticType
);
end;
-function TAstNodeRemover.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
+function TAstNodeRemover.VisitFunctionCall(const Node: IAstNode): IAstNode;
var
- newCallee: IAstNode;
- newArgs: IAstNode;
+ C: IFunctionCallNode;
+ newCallee, newArgs: IAstNode;
begin
- newCallee := EnsureNode(Accept(Node.Callee), Node);
-
- newArgs := Accept(Node.Arguments);
+ C := Node.AsFunctionCall;
+ newCallee := EnsureNode(Accept(C.Callee), Node);
+ newArgs := Accept(C.Arguments);
if newArgs = nil then
- newArgs := TArgumentList.Create([], Node.Arguments.Identity);
+ newArgs := TArgumentList.Create([], C.Arguments.Identity);
- if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then
+ if (newCallee = C.Callee) and (newArgs = C.Arguments) then
Exit(Node);
-
Result :=
- TAst.FunctionCall(
- Node.Identity,
- newCallee,
- newArgs.AsArgumentList,
- Node.StaticType,
- Node.IsTailCall,
- Node.StaticTarget,
- Node.IsTargetPure
- );
+ TAst.FunctionCall(Node.Identity, newCallee, newArgs.AsArgumentList, C.StaticType, C.IsTailCall, C.StaticTarget, C.IsTargetPure);
end;
-function TAstNodeRemover.VisitIndexer(const Node: IIndexerNode): IAstNode;
+function TAstNodeRemover.VisitIndexer(const Node: IAstNode): IAstNode;
var
+ I: IIndexerNode;
newBase, newIndex: IAstNode;
begin
- newBase := EnsureNode(Accept(Node.Base), Node);
- newIndex := EnsureNode(Accept(Node.Index), Node);
-
- if (newBase = Node.Base) and (newIndex = Node.Index) then
+ I := Node.AsIndexer;
+ newBase := EnsureNode(Accept(I.Base), Node);
+ newIndex := EnsureNode(Accept(I.Index), Node);
+ if (newBase = I.Base) and (newIndex = I.Index) then
Exit(Node);
-
- Result := TAst.Indexer(Node.Identity, newBase, newIndex, Node.StaticType);
+ Result := TAst.Indexer(Node.Identity, newBase, newIndex, I.StaticType);
end;
-function TAstNodeRemover.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
+function TAstNodeRemover.VisitMemberAccess(const Node: IAstNode): IAstNode;
var
- newBase: IAstNode;
- newMember: IAstNode;
+ M: IMemberAccessNode;
+ newBase, newMember: IAstNode;
begin
- newBase := EnsureNode(Accept(Node.Base), Node);
-
- newMember := Accept(Node.Member);
+ M := Node.AsMemberAccess;
+ newBase := EnsureNode(Accept(M.Base), Node);
+ newMember := Accept(M.Member);
if newMember = nil then
- begin
- // If member name is deleted, the access is invalid. Replace whole node with Nop.
- Exit(TAst.Nop(Node.Identity));
- end;
+ Exit(TAst.Nop(Node.Identity)); // Member missing -> invalid access
- if (newBase = Node.Base) and (newMember = Node.Member) then
+ if (newBase = M.Base) and (newMember = M.Member) then
Exit(Node);
-
- Result := TAst.MemberAccess(Node.Identity, newBase, newMember.AsKeyword, Node.StaticType);
+ Result := TAst.MemberAccess(Node.Identity, newBase, newMember.AsKeyword, M.StaticType);
end;
-function TAstNodeRemover.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode;
+function TAstNodeRemover.VisitAddSeriesItem(const Node: IAstNode): IAstNode;
var
- newSeries: IAstNode;
- newValue: IAstNode;
- newLookback: IAstNode;
+ A: IAddSeriesItemNode;
+ newSeries, newValue, newLookback: IAstNode;
begin
- newSeries := Accept(Node.Series);
+ A := Node.AsAddSeriesItem;
+ newSeries := Accept(A.Series);
if newSeries = nil then
- Exit(TAst.Nop(Node.Identity)); // Cannot exist without series identifier
+ Exit(TAst.Nop(Node.Identity));
+ newValue := EnsureNode(Accept(A.Value), Node);
+ newLookback := Accept(A.Lookback);
- newValue := EnsureNode(Accept(Node.Value), Node);
- newLookback := Accept(Node.Lookback); // Optional
-
- if (newSeries = Node.Series) and (newValue = Node.Value) and (newLookback = Node.Lookback) then
+ if (newSeries = A.Series) and (newValue = A.Value) and (newLookback = A.Lookback) then
Exit(Node);
-
- Result := TAst.AddSeriesItem(Node.Identity, newSeries.AsIdentifier, newValue, newLookback, Node.StaticType);
+ Result := TAst.AddSeriesItem(Node.Identity, newSeries.AsIdentifier, newValue, newLookback, A.StaticType);
end;
end.
diff --git a/Src/AST/Myc.Ast.Script.Print.pas b/Src/AST/Myc.Ast.Script.Print.pas
new file mode 100644
index 0000000..a375205
--- /dev/null
+++ b/Src/AST/Myc.Ast.Script.Print.pas
@@ -0,0 +1,537 @@
+unit Myc.Ast.Script.Print;
+
+interface
+
+uses
+ System.SysUtils,
+ System.Classes,
+ Myc.Data.Value,
+ Myc.Data.Scalar,
+ Myc.Ast.Nodes,
+ Myc.Ast.Visitor,
+ Myc.Ast;
+
+type
+ TPrettyPrintVisitor = class(TAstVisitor)
+ strict private
+ FBuilder: TStringBuilder;
+ FIndentLevel: Integer;
+ procedure Indent;
+ procedure Unindent;
+ procedure Append(const S: string);
+ procedure NewLine;
+
+ // Handlers now strictly accept IAstNode
+ function VisitConstant(const N: IAstNode): TVoid;
+ function VisitIdentifier(const N: IAstNode): TVoid;
+ function VisitKeyword(const N: IAstNode): TVoid;
+
+ function VisitParameterList(const N: IAstNode): TVoid;
+ function VisitArgumentList(const N: IAstNode): TVoid;
+ function VisitExpressionList(const N: IAstNode): TVoid;
+ function VisitRecordFieldList(const N: IAstNode): TVoid;
+ function VisitRecordField(const N: IAstNode): TVoid;
+
+ function VisitIfExpression(const N: IAstNode): TVoid;
+ function VisitCondExpression(const N: IAstNode): TVoid;
+ function VisitLambdaExpression(const N: IAstNode): TVoid;
+ function VisitMacroDefinition(const N: IAstNode): TVoid;
+ function VisitQuasiquote(const N: IAstNode): TVoid;
+ function VisitUnquote(const N: IAstNode): TVoid;
+ function VisitUnquoteSplicing(const N: IAstNode): TVoid;
+ function VisitFunctionCall(const N: IAstNode): TVoid;
+ function VisitMacroExpansionNode(const N: IAstNode): TVoid;
+ function VisitBlockExpression(const N: IAstNode): TVoid;
+ function VisitVariableDeclaration(const N: IAstNode): TVoid;
+ function VisitAssignment(const N: IAstNode): TVoid;
+ function VisitIndexer(const N: IAstNode): TVoid;
+ function VisitMemberAccess(const N: IAstNode): TVoid;
+ function VisitRecordLiteral(const N: IAstNode): TVoid;
+ function VisitCreateSeries(const N: IAstNode): TVoid;
+ function VisitAddSeriesItem(const N: IAstNode): TVoid;
+ function VisitSeriesLength(const N: IAstNode): TVoid;
+ function VisitRecurNode(const N: IAstNode): TVoid;
+ function VisitNop(const N: IAstNode): TVoid;
+
+ function VisitPipeInput(const N: IAstNode): TVoid;
+ function VisitPipeSelectorList(const N: IAstNode): TVoid;
+ function VisitPipeInputList(const N: IAstNode): TVoid;
+ function VisitPipe(const N: IAstNode): TVoid;
+
+ protected
+ procedure SetupHandlers; override;
+
+ public
+ constructor Create;
+ destructor Destroy; override;
+ function GetResult: string;
+ procedure Execute(const RootNode: IAstNode);
+ end;
+
+implementation
+
+{ TPrettyPrintVisitor }
+
+constructor TPrettyPrintVisitor.Create;
+begin
+ inherited Create;
+ FBuilder := TStringBuilder.Create;
+ FIndentLevel := 0;
+end;
+
+destructor TPrettyPrintVisitor.Destroy;
+begin
+ FBuilder.Free;
+ inherited;
+end;
+
+procedure TPrettyPrintVisitor.SetupHandlers;
+begin
+ Register(akConstant, VisitConstant);
+ Register(akIdentifier, VisitIdentifier);
+ Register(akKeyword, VisitKeyword);
+
+ Register(akParameterList, VisitParameterList);
+ Register(akArgumentList, VisitArgumentList);
+ Register(akExpressionList, VisitExpressionList);
+ Register(akRecordFieldList, VisitRecordFieldList);
+ Register(akRecordField, VisitRecordField);
+
+ Register(akIfExpression, VisitIfExpression);
+ Register(akCondExpression, VisitCondExpression);
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akFunctionCall, VisitFunctionCall);
+ Register(akMacroExpansion, VisitMacroExpansionNode);
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+ Register(akAssignment, VisitAssignment);
+ Register(akMacroDefinition, VisitMacroDefinition);
+ Register(akQuasiquote, VisitQuasiquote);
+ Register(akUnquote, VisitUnquote);
+ Register(akUnquoteSplicing, VisitUnquoteSplicing);
+ Register(akIndexer, VisitIndexer);
+ Register(akMemberAccess, VisitMemberAccess);
+ Register(akRecordLiteral, VisitRecordLiteral);
+ Register(akCreateSeries, VisitCreateSeries);
+ Register(akAddSeriesItem, VisitAddSeriesItem);
+ Register(akSeriesLength, VisitSeriesLength);
+ Register(akRecur, VisitRecurNode);
+ Register(akNop, VisitNop);
+
+ Register(akPipeInput, VisitPipeInput);
+ Register(akPipeSelectorList, VisitPipeSelectorList);
+ Register(akPipeInputList, VisitPipeInputList);
+ Register(akPipe, VisitPipe);
+end;
+
+function TPrettyPrintVisitor.GetResult: string;
+begin
+ Result := FBuilder.ToString;
+end;
+
+procedure TPrettyPrintVisitor.Execute(const RootNode: IAstNode);
+begin
+ if Assigned(RootNode) then
+ Visit(RootNode);
+end;
+
+procedure TPrettyPrintVisitor.Indent;
+begin
+ Inc(FIndentLevel, 2);
+end;
+
+procedure TPrettyPrintVisitor.Unindent;
+begin
+ Dec(FIndentLevel, 2);
+end;
+
+procedure TPrettyPrintVisitor.Append(const S: string);
+begin
+ FBuilder.Append(S);
+end;
+
+procedure TPrettyPrintVisitor.NewLine;
+begin
+ FBuilder.AppendLine;
+ FBuilder.Append(''.PadLeft(FIndentLevel));
+end;
+
+// --- Implementations (Clean & Typed) ---
+
+function TPrettyPrintVisitor.VisitPipeInput(const N: IAstNode): TVoid;
+var
+ P: IPipeInputNode;
+begin
+ P := N.AsPipeInput;
+ Visit(P.StreamSource);
+ Append(' ');
+ Visit(P.Selectors);
+end;
+
+function TPrettyPrintVisitor.VisitPipeSelectorList(const N: IAstNode): TVoid;
+var
+ L: IPipeSelectorList;
+ i: Integer;
+begin
+ L := N.AsPipeSelectorList;
+ Append('[');
+ for i := 0 to L.Count - 1 do
+ begin
+ if i > 0 then
+ Append(' ');
+ Visit(L[i]);
+ end;
+ Append(']');
+end;
+
+function TPrettyPrintVisitor.VisitPipeInputList(const N: IAstNode): TVoid;
+var
+ L: IPipeInputList;
+ i: Integer;
+begin
+ L := N.AsPipeInputList;
+ Append('[');
+ for i := 0 to L.Count - 1 do
+ begin
+ if i > 0 then
+ Append(' ');
+ Visit(L[i]);
+ end;
+ Append(']');
+end;
+
+function TPrettyPrintVisitor.VisitPipe(const N: IAstNode): TVoid;
+var
+ P: IPipeNode;
+begin
+ P := N.AsPipe;
+ Append('(pipe ');
+ Visit(P.Inputs);
+ Indent;
+ NewLine;
+ Visit(P.Transformation);
+ Unindent;
+ NewLine;
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitConstant(const N: IAstNode): TVoid;
+var
+ C: IConstantNode;
+begin
+ C := N.AsConstant;
+ if C.Value.Kind = vkText then
+ Append('"' + C.Value.AsText + '"')
+ else
+ Append(C.Value.ToString);
+end;
+
+function TPrettyPrintVisitor.VisitIdentifier(const N: IAstNode): TVoid;
+begin
+ Append(N.AsIdentifier.Name);
+end;
+
+function TPrettyPrintVisitor.VisitKeyword(const N: IAstNode): TVoid;
+begin
+ Append(':' + N.AsKeyword.Value.Name);
+end;
+
+function TPrettyPrintVisitor.VisitNop(const N: IAstNode): TVoid;
+begin
+ Append('...');
+end;
+
+function TPrettyPrintVisitor.VisitParameterList(const N: IAstNode): TVoid;
+var
+ L: IParameterList;
+ i: Integer;
+begin
+ L := N.AsParameterList;
+ Append('[');
+ for i := 0 to L.Count - 1 do
+ begin
+ if i > 0 then
+ Append(' ');
+ Visit(L[i]);
+ end;
+ Append(']');
+end;
+
+function TPrettyPrintVisitor.VisitArgumentList(const N: IAstNode): TVoid;
+var
+ L: IArgumentList;
+ i: Integer;
+begin
+ L := N.AsArgumentList;
+ for i := 0 to L.Count - 1 do
+ begin
+ Append(' ');
+ Visit(L[i]);
+ end;
+end;
+
+function TPrettyPrintVisitor.VisitExpressionList(const N: IAstNode): TVoid;
+var
+ L: IExpressionList;
+ item: IAstNode;
+begin
+ L := N.AsExpressionList;
+ Indent;
+ for item in L do
+ begin
+ NewLine;
+ Visit(item);
+ end;
+ Unindent;
+ NewLine;
+end;
+
+function TPrettyPrintVisitor.VisitRecordFieldList(const N: IAstNode): TVoid;
+var
+ L: IRecordFieldList;
+ item: IRecordFieldNode;
+begin
+ L := N.AsRecordFieldList;
+ Indent;
+ for item in L do
+ begin
+ NewLine;
+ Visit(item);
+ end;
+ Unindent;
+ NewLine;
+end;
+
+function TPrettyPrintVisitor.VisitRecordField(const N: IAstNode): TVoid;
+var
+ F: IRecordFieldNode;
+begin
+ F := N.AsRecordField;
+ Visit(F.Key);
+ Append(' ');
+ Visit(F.Value);
+end;
+
+function TPrettyPrintVisitor.VisitIfExpression(const N: IAstNode): TVoid;
+var
+ E: IIfExpressionNode;
+begin
+ E := N.AsIfExpression;
+ Append('(if ');
+ Visit(E.Condition);
+ Indent;
+ NewLine;
+ Visit(E.ThenBranch);
+ if Assigned(E.ElseBranch) then
+ begin
+ NewLine;
+ Visit(E.ElseBranch);
+ end;
+ Unindent;
+ NewLine;
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitCondExpression(const N: IAstNode): TVoid;
+var
+ E: ICondExpressionNode;
+begin
+ E := N.AsCondExpression;
+ Append('(?');
+ Indent;
+ for var pair in E.Pairs do
+ begin
+ NewLine;
+ Visit(pair.Condition);
+ Append(' ');
+ Visit(pair.Branch);
+ end;
+ NewLine;
+ Visit(E.ElseBranch);
+ Unindent;
+ NewLine;
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitLambdaExpression(const N: IAstNode): TVoid;
+var
+ E: ILambdaExpressionNode;
+begin
+ E := N.AsLambdaExpression;
+ Append('(fn ');
+ Visit(E.Parameters);
+ Indent;
+ NewLine;
+ Visit(E.Body);
+ Unindent;
+ NewLine;
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitMacroDefinition(const N: IAstNode): TVoid;
+var
+ M: IMacroDefinitionNode;
+begin
+ M := N.AsMacroDefinition;
+ Append('(defmacro ');
+ Visit(M.Name);
+ Append(' ');
+ Visit(M.Parameters);
+ Indent;
+ NewLine;
+ Visit(M.Body);
+ Unindent;
+ NewLine;
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitQuasiquote(const N: IAstNode): TVoid;
+begin
+ Append('`');
+ Visit(N.AsQuasiquote.Expression);
+end;
+
+function TPrettyPrintVisitor.VisitUnquote(const N: IAstNode): TVoid;
+begin
+ Append('~');
+ Visit(N.AsUnquote.Expression);
+end;
+
+function TPrettyPrintVisitor.VisitUnquoteSplicing(const N: IAstNode): TVoid;
+begin
+ Append('~@');
+ Visit(N.AsUnquoteSplicing.Expression);
+end;
+
+function TPrettyPrintVisitor.VisitFunctionCall(const N: IAstNode): TVoid;
+var
+ C: IFunctionCallNode;
+begin
+ C := N.AsFunctionCall;
+ if (C.Callee.Kind = akIdentifier) and (C.Callee.AsIdentifier.Name = 'quote') and (C.Arguments.Count = 1) then
+ begin
+ Append('''');
+ Visit(C.Arguments[0]);
+ exit;
+ end;
+ Append('(');
+ Visit(C.Callee);
+ Visit(C.Arguments);
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitMacroExpansionNode(const N: IAstNode): TVoid;
+begin
+ Visit(N.AsMacroExpansion.CallNode);
+end;
+
+function TPrettyPrintVisitor.VisitRecurNode(const N: IAstNode): TVoid;
+begin
+ Append('(recur');
+ Visit(N.AsRecur.Arguments);
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitBlockExpression(const N: IAstNode): TVoid;
+begin
+ Append('(do');
+ Visit(N.AsBlockExpression.Expressions);
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitVariableDeclaration(const N: IAstNode): TVoid;
+var
+ V: IVariableDeclarationNode;
+begin
+ V := N.AsVariableDeclaration;
+ Append('(def ');
+ Visit(V.Target);
+ if Assigned(V.Initializer) then
+ begin
+ Append(' ');
+ Visit(V.Initializer);
+ end;
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitAssignment(const N: IAstNode): TVoid;
+var
+ A: IAssignmentNode;
+begin
+ A := N.AsAssignment;
+ Append('(assign ');
+ Visit(A.Target);
+ Append(' ');
+ Visit(A.Value);
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitIndexer(const N: IAstNode): TVoid;
+var
+ I: IIndexerNode;
+begin
+ I := N.AsIndexer;
+ Append('(get ');
+ Visit(I.Base);
+ Append(' ');
+ Visit(I.Index);
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitMemberAccess(const N: IAstNode): TVoid;
+var
+ M: IMemberAccessNode;
+begin
+ M := N.AsMemberAccess;
+ Append('(.');
+ Visit(M.Member);
+ Append(' ');
+ Visit(M.Base);
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitRecordLiteral(const N: IAstNode): TVoid;
+var
+ R: IRecordLiteralNode;
+begin
+ R := N.AsRecordLiteral;
+ if R.Fields.Count = 0 then
+ begin
+ Append('{}');
+ exit;
+ end;
+ Append('{');
+ Visit(R.Fields);
+ Append('}');
+end;
+
+function TPrettyPrintVisitor.VisitCreateSeries(const N: IAstNode): TVoid;
+begin
+ Append(Format('(new-series "%s")', [N.AsCreateSeries.Definition]));
+end;
+
+function TPrettyPrintVisitor.VisitAddSeriesItem(const N: IAstNode): TVoid;
+var
+ A: IAddSeriesItemNode;
+begin
+ A := N.AsAddSeriesItem;
+ Append('(add-item ');
+ Visit(A.Series);
+ Append(' ');
+ Visit(A.Value);
+ if Assigned(A.Lookback) then
+ begin
+ Append(' ');
+ Visit(A.Lookback);
+ end;
+ Append(')');
+end;
+
+function TPrettyPrintVisitor.VisitSeriesLength(const N: IAstNode): TVoid;
+begin
+ Append('(count ');
+ Visit(N.AsSeriesLength.Series);
+ Append(')');
+end;
+
+end.
diff --git a/Src/AST/Myc.Ast.Script.pas b/Src/AST/Myc.Ast.Script.pas
index 7a36c30..97772fc 100644
--- a/Src/AST/Myc.Ast.Script.pas
+++ b/Src/AST/Myc.Ast.Script.pas
@@ -6,7 +6,8 @@ uses
System.SysUtils,
Myc.Data.Value,
Myc.Ast.Nodes,
- Myc.Ast.Visitor;
+ Myc.Ast.Visitor,
+ Myc.Ast.Script.Print;
type
EParserException = class(EAstException)
@@ -116,57 +117,6 @@ type
function Parse: IAstNode;
end;
- TPrettyPrintVisitor = class(TAstVisitor)
- private
- FBuilder: TStringBuilder;
- FIndentLevel: Integer;
- procedure Indent;
- procedure Unindent;
- procedure Append(const S: string);
- procedure NewLine;
- public
- constructor Create;
- destructor Destroy; override;
- function GetResult: string;
- function Execute(const RootNode: IAstNode): TDataValue;
-
- function VisitConstant(const Node: IConstantNode): TVoid; override;
- function VisitIdentifier(const Node: IIdentifierNode): TVoid; override;
- function VisitKeyword(const Node: IKeywordNode): TVoid; override;
-
- function VisitParameterList(const Node: IParameterList): TVoid; override;
- function VisitArgumentList(const Node: IArgumentList): TVoid; override;
- function VisitExpressionList(const Node: IExpressionList): TVoid; override;
- function VisitRecordFieldList(const Node: IRecordFieldList): TVoid; override;
- function VisitRecordField(const Node: IRecordFieldNode): TVoid; override;
-
- function VisitIfExpression(const Node: IIfExpressionNode): TVoid; override;
- function VisitCondExpression(const Node: ICondExpressionNode): TVoid; override;
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): TVoid; override;
- function VisitMacroDefinition(const Node: IMacroDefinitionNode): TVoid; override;
- function VisitQuasiquote(const Node: IQuasiquoteNode): TVoid; override;
- function VisitUnquote(const Node: IUnquoteNode): TVoid; override;
- function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TVoid; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): TVoid; override;
- function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TVoid; override;
- function VisitBlockExpression(const Node: IBlockExpressionNode): TVoid; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TVoid; override;
- function VisitAssignment(const Node: IAssignmentNode): TVoid; override;
- function VisitIndexer(const Node: IIndexerNode): TVoid; override;
- function VisitMemberAccess(const Node: IMemberAccessNode): TVoid; override;
- function VisitRecordLiteral(const Node: IRecordLiteralNode): TVoid; override;
- function VisitCreateSeries(const Node: ICreateSeriesNode): TVoid; override;
- function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TVoid; override;
- function VisitSeriesLength(const Node: ISeriesLengthNode): TVoid; override;
- function VisitRecurNode(const Node: IRecurNode): TVoid; override;
- function VisitNop(const Node: INopNode): TVoid; override;
-
- function VisitPipeInput(const Node: IPipeInputNode): TVoid; override;
- function VisitPipeSelectorList(const Node: IPipeSelectorList): TVoid; override;
- function VisitPipeInputList(const Node: IPipeInputList): TVoid; override;
- function VisitPipe(const Node: IPipeNode): TVoid; override;
- end;
-
{ TTokenKindHelper }
function TTokenKindHelper.ToString: String;
begin
@@ -812,362 +762,6 @@ begin
Result := expr.Node;
end;
-{ TPrettyPrintVisitor }
-
-constructor TPrettyPrintVisitor.Create;
-begin
- inherited Create;
- FBuilder := TStringBuilder.Create;
- FIndentLevel := 0;
-end;
-
-destructor TPrettyPrintVisitor.Destroy;
-begin
- FBuilder.Free;
- inherited;
-end;
-
-function TPrettyPrintVisitor.GetResult: string;
-begin
- Result := FBuilder.ToString;
-end;
-
-procedure TPrettyPrintVisitor.Indent;
-begin
- Inc(FIndentLevel, 2);
-end;
-
-procedure TPrettyPrintVisitor.Unindent;
-begin
- Dec(FIndentLevel, 2);
-end;
-
-procedure TPrettyPrintVisitor.Append(const S: string);
-begin
- FBuilder.Append(S);
-end;
-
-procedure TPrettyPrintVisitor.NewLine;
-begin
- FBuilder.AppendLine;
- FBuilder.Append(''.PadLeft(FIndentLevel));
-end;
-
-function TPrettyPrintVisitor.Execute(const RootNode: IAstNode): TDataValue;
-begin
- if Assigned(RootNode) then
- RootNode.Accept(Self);
- Result := TDataValue.Void;
-end;
-
-function TPrettyPrintVisitor.VisitPipeInput(const Node: IPipeInputNode): TVoid;
-begin
- Node.StreamSource.Accept(Self);
- Append(' ');
- // Printer Logic for Vector syntax
- Node.Selectors.Accept(Self); // Selectors is IPipeSelectorList which is visited as list
-end;
-
-function TPrettyPrintVisitor.VisitPipeSelectorList(const Node: IPipeSelectorList): TVoid;
-begin
- Append('[');
- for var i := 0 to Node.Count - 1 do
- begin
- if i > 0 then
- Append(' ');
- Node[i].Accept(Self);
- end;
- Append(']');
-end;
-
-function TPrettyPrintVisitor.VisitPipeInputList(const Node: IPipeInputList): TVoid;
-begin
- Append('[');
- for var i := 0 to Node.Count - 1 do
- begin
- if i > 0 then
- Append(' ');
- Node[i].Accept(Self);
- end;
- Append(']');
-end;
-
-function TPrettyPrintVisitor.VisitPipe(const Node: IPipeNode): TVoid;
-begin
- Append('(pipe ');
- Node.Inputs.Accept(Self);
- Indent;
- NewLine;
- Node.Transformation.Accept(Self);
- Unindent;
- NewLine;
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitConstant(const Node: IConstantNode): TVoid;
-begin
- if Node.Value.Kind = vkText then
- Append('"' + Node.Value.AsText + '"')
- else
- Append(Node.Value.ToString);
-end;
-
-function TPrettyPrintVisitor.VisitIdentifier(const Node: IIdentifierNode): TVoid;
-begin
- Append(Node.Name);
-end;
-
-function TPrettyPrintVisitor.VisitKeyword(const Node: IKeywordNode): TVoid;
-begin
- Append(':' + Node.Value.Name);
-end;
-
-function TPrettyPrintVisitor.VisitNop(const Node: INopNode): TVoid;
-begin
- Append('...');
-end;
-
-function TPrettyPrintVisitor.VisitParameterList(const Node: IParameterList): TVoid;
-begin
- Append('[');
- for var i := 0 to Node.Count - 1 do
- begin
- if i > 0 then
- Append(' ');
- Node[i].Accept(Self);
- end;
- Append(']');
-end;
-
-function TPrettyPrintVisitor.VisitArgumentList(const Node: IArgumentList): TVoid;
-begin
- for var i := 0 to Node.Count - 1 do
- begin
- Append(' ');
- Node[i].Accept(Self);
- end;
-end;
-
-function TPrettyPrintVisitor.VisitExpressionList(const Node: IExpressionList): TVoid;
-begin
- Indent;
- for var item in Node do
- begin
- NewLine;
- item.Accept(Self);
- end;
- Unindent;
- NewLine;
-end;
-
-function TPrettyPrintVisitor.VisitRecordFieldList(const Node: IRecordFieldList): TVoid;
-begin
- Indent;
- for var item in Node do
- begin
- NewLine;
- item.Accept(Self);
- end;
- Unindent;
- NewLine;
-end;
-
-function TPrettyPrintVisitor.VisitRecordField(const Node: IRecordFieldNode): TVoid;
-begin
- Node.Key.Accept(Self);
- Append(' ');
- Node.Value.Accept(Self);
-end;
-
-function TPrettyPrintVisitor.VisitIfExpression(const Node: IIfExpressionNode): TVoid;
-begin
- Append('(if ');
- Node.Condition.Accept(Self);
- Indent;
- NewLine;
- Node.ThenBranch.Accept(Self);
- if Assigned(Node.ElseBranch) then
- begin
- NewLine;
- Node.ElseBranch.Accept(Self);
- end;
- Unindent;
- NewLine;
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitCondExpression(const Node: ICondExpressionNode): TVoid;
-begin
- Append('(?');
- Indent;
- for var pair in Node.Pairs do
- begin
- NewLine;
- pair.Condition.Accept(Self);
- Append(' ');
- pair.Branch.Accept(Self);
- end;
- NewLine;
- Node.ElseBranch.Accept(Self);
- Unindent;
- NewLine;
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TVoid;
-begin
- Append('(fn ');
- Node.Parameters.Accept(Self);
- Indent;
- NewLine;
- Node.Body.Accept(Self);
- Unindent;
- NewLine;
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TVoid;
-begin
- Append('(defmacro ');
- Node.Name.Accept(Self);
- Append(' ');
- Node.Parameters.Accept(Self);
- Indent;
- NewLine;
- Node.Body.Accept(Self);
- Unindent;
- NewLine;
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): TVoid;
-begin
- Append('`');
- Node.Expression.Accept(Self);
-end;
-
-function TPrettyPrintVisitor.VisitUnquote(const Node: IUnquoteNode): TVoid;
-begin
- Append('~');
- Node.Expression.Accept(Self);
-end;
-
-function TPrettyPrintVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TVoid;
-begin
- Append('~@');
- Node.Expression.Accept(Self);
-end;
-
-function TPrettyPrintVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TVoid;
-begin
- if (Node.Callee.Kind = akIdentifier) and (Node.Callee.AsIdentifier.Name = 'quote') and (Node.Arguments.Count = 1) then
- begin
- Append('''');
- Node.Arguments[0].Accept(Self);
- exit;
- end;
- Append('(');
- Node.Callee.Accept(Self);
- Node.Arguments.Accept(Self);
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TVoid;
-begin
- VisitFunctionCall(Node.CallNode);
-end;
-
-function TPrettyPrintVisitor.VisitRecurNode(const Node: IRecurNode): TVoid;
-begin
- Append('(recur');
- Node.Arguments.Accept(Self);
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TVoid;
-begin
- Append('(do');
- Node.Expressions.Accept(Self);
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TVoid;
-begin
- Append('(def ');
- Node.Target.Accept(Self);
- if Assigned(Node.Initializer) then
- begin
- Append(' ');
- Node.Initializer.Accept(Self);
- end;
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitAssignment(const Node: IAssignmentNode): TVoid;
-begin
- Append('(assign ');
- Node.Target.Accept(Self);
- Append(' ');
- Node.Value.Accept(Self);
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitIndexer(const Node: IIndexerNode): TVoid;
-begin
- Append('(get ');
- Node.Base.Accept(Self);
- Append(' ');
- Node.Index.Accept(Self);
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TVoid;
-begin
- Append('(.');
- Node.Member.Accept(Self);
- Append(' ');
- Node.Base.Accept(Self);
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TVoid;
-begin
- if Node.Fields.Count = 0 then
- begin
- Append('{}');
- exit;
- end;
- Append('{');
- Node.Fields.Accept(Self);
- Append('}');
-end;
-
-function TPrettyPrintVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TVoid;
-begin
- Append(Format('(new-series "%s")', [Node.Definition]));
-end;
-
-function TPrettyPrintVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TVoid;
-begin
- Append('(add-item ');
- Node.Series.Accept(Self);
- Append(' ');
- Node.Value.Accept(Self);
- if Assigned(Node.Lookback) then
- begin
- Append(' ');
- Node.Lookback.Accept(Self);
- end;
- Append(')');
-end;
-
-function TPrettyPrintVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TVoid;
-begin
- Append('(count ');
- Node.Series.Accept(Self);
- Append(')');
-end;
-
{ TAstScript }
class function TAstScript.Parse(const ASource: string): IAstNode;
diff --git a/Src/AST/Myc.Ast.Visitor.pas b/Src/AST/Myc.Ast.Visitor.pas
index 4598b66..4999a63 100644
--- a/Src/AST/Myc.Ast.Visitor.pas
+++ b/Src/AST/Myc.Ast.Visitor.pas
@@ -14,73 +14,33 @@ uses
type
TAstVisitor = class;
- // Delegate for handling a specific node kind within the visitor context.
- // Receives the visitor instance explicitly to allow for stateless handlers or external logic.
- TNodeHandler = reference to function(const Visitor: TAstVisitor; const Node: IAstNode): T;
-
- // Callback to register a handler for a specific node kind.
- TRegisterHandler = reference to procedure(Kind: TAstNodeKind; const Handler: TNodeHandler);
+ // Handler signature is now strictly based on IAstNode to ensure binary compatibility
+ // with the dispatch array.
+ TNodeHandler = reference to function(const Node: IAstNode): T;
TAstVisitor = class abstract(TInterfacedObject, IAstVisitor)
strict private
- // The registry for O(1) dispatch based on TAstNodeKind
FHandlers: array[TAstNodeKind] of TNodeHandler;
- // IAstVisitor explicit implementation (bridge methods)
+ // IAstVisitor bridge
function IAstVisitor.Visit = VisitBridge;
-
- // Private helpers to wrap T into TDataValue for the non-generic interface
function VisitBridge(const Node: IAstNode): TDataValue;
+ // Sentinel to fill gaps (Fail Fast protection)
+ function DefaultSentinel(const Node: IAstNode): T;
+
protected
- // Initial setup of the dispatch table.
- // The base implementation maps all kinds to the legacy VisitXYZ virtual methods.
- procedure SetupHandlers(const Register: TRegisterHandler); virtual;
+ // --- Registration ---
+
+ // Register a handler. No generic overload anymore.
+ procedure Register(Kind: TAstNodeKind; const Handler: TNodeHandler); virtual;
+
+ // Subclasses implement this to call Register()
+ procedure SetupHandlers; virtual;
+
+ // --- Dispatch ---
- // The central dispatch method. Looks up the handler in the registry.
function Visit(const Node: IAstNode): T; virtual;
-
- // Legacy Virtual Methods (kept for backward compatibility and default mapping)
- // Subclasses should eventually move to RegisterHandler, but overriding these still works
- // because SetupHandlers maps them by default.
- function VisitConstant(const Node: IConstantNode): T; virtual;
- function VisitIdentifier(const Node: IIdentifierNode): T; virtual;
- function VisitKeyword(const Node: IKeywordNode): T; virtual;
-
- function VisitParameterList(const Node: IParameterList): T; virtual;
- function VisitArgumentList(const Node: IArgumentList): T; virtual;
- function VisitExpressionList(const Node: IExpressionList): T; virtual;
- function VisitRecordFieldList(const Node: IRecordFieldList): T; virtual;
- function VisitRecordField(const Node: IRecordFieldNode): T; virtual;
-
- function VisitIfExpression(const Node: IIfExpressionNode): T; virtual;
- function VisitCondExpression(const Node: ICondExpressionNode): T; virtual;
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): T; virtual;
- function VisitFunctionCall(const Node: IFunctionCallNode): T; virtual;
- function VisitMacroExpansionNode(const Node: IMacroExpansionNode): T; virtual;
- function VisitBlockExpression(const Node: IBlockExpressionNode): T; virtual;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): T; virtual;
- function VisitAssignment(const Node: IAssignmentNode): T; virtual;
- function VisitMacroDefinition(const Node: IMacroDefinitionNode): T; virtual;
- function VisitQuasiquote(const Node: IQuasiquoteNode): T; virtual;
- function VisitUnquote(const Node: IUnquoteNode): T; virtual;
- function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): T; virtual;
- function VisitIndexer(const Node: IIndexerNode): T; virtual;
- function VisitMemberAccess(const Node: IMemberAccessNode): T; virtual;
- function VisitRecordLiteral(const Node: IRecordLiteralNode): T; virtual;
- function VisitCreateSeries(const Node: ICreateSeriesNode): T; virtual;
- function VisitAddSeriesItem(const Node: IAddSeriesItemNode): T; virtual;
- function VisitSeriesLength(const Node: ISeriesLengthNode): T; virtual;
- function VisitRecurNode(const Node: IRecurNode): T; virtual;
- function VisitNop(const Node: INopNode): T; virtual;
-
- function VisitPipeInput(const Node: IPipeInputNode): T; virtual;
- function VisitPipeSelectorList(const Node: IPipeSelectorList): T; virtual;
- function VisitPipeInputList(const Node: IPipeInputList): T; virtual;
- function VisitPipe(const Node: IPipeNode): T; virtual;
-
- // Bridge to support calling Accept on node directly (double dispatch pattern)
- // In the new architecture, we prefer calling Visit(Node) directly.
function Accept(const Node: IAstNode): T; virtual;
public
@@ -89,61 +49,108 @@ type
end;
// Helper for transformation visitors (returns IAstNode)
- // Overrides legacy methods to provide default recursive reconstruction behavior.
+ // Implements recursive reconstruction: If a child changes, a new node is returned.
TAstTransformer = class abstract(TAstVisitor)
protected
- function VisitConstant(const Node: IConstantNode): IAstNode; override;
- function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
- function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
+ // Leaves
+ function VisitLeaf(const N: IAstNode): IAstNode;
- function VisitParameterList(const Node: IParameterList): IAstNode; override;
- function VisitArgumentList(const Node: IArgumentList): IAstNode; override;
- function VisitExpressionList(const Node: IExpressionList): IAstNode; override;
- function VisitRecordFieldList(const Node: IRecordFieldList): IAstNode; override;
- function VisitRecordField(const Node: IRecordFieldNode): IAstNode; override;
+ // Lists
+ function VisitParameterList(const N: IAstNode): IAstNode;
+ function VisitArgumentList(const N: IAstNode): IAstNode;
+ function VisitExpressionList(const N: IAstNode): IAstNode;
+ function VisitRecordFieldList(const N: IAstNode): IAstNode;
+ function VisitRecordField(const N: IAstNode): IAstNode;
- function VisitIfExpression(const Node: IIfExpressionNode): IAstNode; override;
- function VisitCondExpression(const Node: ICondExpressionNode): IAstNode; override;
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
- function VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode; override;
- function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
- function VisitAssignment(const Node: IAssignmentNode): IAstNode; override;
- function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override;
- function VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; override;
- function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
- function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override;
- function VisitIndexer(const Node: IIndexerNode): IAstNode; override;
- function VisitMemberAccess(const Node: IMemberAccessNode): IAstNode; override;
- function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
- function VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode; override;
- function VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode; override;
- function VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode; override;
- function VisitRecurNode(const Node: IRecurNode): IAstNode; override;
- function VisitNop(const Node: INopNode): IAstNode; override;
+ // Structures
+ function VisitIfExpression(const N: IAstNode): IAstNode;
+ function VisitCondExpression(const N: IAstNode): IAstNode;
+ function VisitLambdaExpression(const N: IAstNode): IAstNode;
+ function VisitFunctionCall(const N: IAstNode): IAstNode;
+ function VisitMacroExpansionNode(const N: IAstNode): IAstNode;
+ function VisitBlockExpression(const N: IAstNode): IAstNode;
+ function VisitVariableDeclaration(const N: IAstNode): IAstNode;
+ function VisitAssignment(const N: IAstNode): IAstNode;
+ function VisitMacroDefinition(const N: IAstNode): IAstNode;
- function VisitPipeInput(const Node: IPipeInputNode): IAstNode; override;
- function VisitPipeSelectorList(const Node: IPipeSelectorList): IAstNode; override;
- function VisitPipeInputList(const Node: IPipeInputList): IAstNode; override;
- function VisitPipe(const Node: IPipeNode): IAstNode; override;
+ // Wrappers
+ function VisitQuasiquote(const N: IAstNode): IAstNode;
+ function VisitUnquote(const N: IAstNode): IAstNode;
+ function VisitUnquoteSplicing(const N: IAstNode): IAstNode;
+
+ function VisitIndexer(const N: IAstNode): IAstNode;
+ function VisitMemberAccess(const N: IAstNode): IAstNode;
+ function VisitRecordLiteral(const N: IAstNode): IAstNode;
+ function VisitAddSeriesItem(const N: IAstNode): IAstNode;
+ function VisitSeriesLength(const N: IAstNode): IAstNode;
+ function VisitRecurNode(const N: IAstNode): IAstNode;
+
+ // Pipes
+ function VisitPipeInput(const N: IAstNode): IAstNode;
+ function VisitPipeSelectorList(const N: IAstNode): IAstNode;
+ function VisitPipeInputList(const N: IAstNode): IAstNode;
+ function VisitPipe(const N: IAstNode): IAstNode;
+
+ procedure SetupHandlers; override;
end;
TVoid = record
end;
- TAstVisitor = class(TAstVisitor);
+ // A concrete "Walker" that traverses all children by default.
+ // Useful for Analyzers, Linters, or Searchers.
+ TAstVisitor = class(TAstVisitor)
+ protected
+ // Walker implementations
+ function WalkLeaf(const N: IAstNode): TVoid;
+
+ function WalkParameterList(const N: IAstNode): TVoid;
+ function WalkArgumentList(const N: IAstNode): TVoid;
+ function WalkExpressionList(const N: IAstNode): TVoid;
+ function WalkRecordFieldList(const N: IAstNode): TVoid;
+ function WalkRecordField(const N: IAstNode): TVoid;
+
+ function WalkIfExpression(const N: IAstNode): TVoid;
+ function WalkCondExpression(const N: IAstNode): TVoid;
+ function WalkLambdaExpression(const N: IAstNode): TVoid;
+ function WalkFunctionCall(const N: IAstNode): TVoid;
+ function WalkMacroExpansionNode(const N: IAstNode): TVoid;
+ function WalkBlockExpression(const N: IAstNode): TVoid;
+ function WalkVariableDeclaration(const N: IAstNode): TVoid;
+ function WalkAssignment(const N: IAstNode): TVoid;
+ function WalkMacroDefinition(const N: IAstNode): TVoid;
+ function WalkWrapper(const N: IAstNode): TVoid; // Quasiquote, Unquote, etc.
+ function WalkIndexer(const N: IAstNode): TVoid;
+ function WalkMemberAccess(const N: IAstNode): TVoid;
+ function WalkRecordLiteral(const N: IAstNode): TVoid;
+ function WalkAddSeriesItem(const N: IAstNode): TVoid;
+ function WalkSeriesLength(const N: IAstNode): TVoid;
+ function WalkRecurNode(const N: IAstNode): TVoid;
+
+ function WalkPipeInput(const N: IAstNode): TVoid;
+ function WalkPipeSelectorList(const N: IAstNode): TVoid;
+ function WalkPipeInputList(const N: IAstNode): TVoid;
+ function WalkPipe(const N: IAstNode): TVoid;
+
+ procedure SetupHandlers; override;
+ end;
implementation
{ TAstVisitor }
procedure TAstVisitor.AfterConstruction;
+var
+ k: TAstNodeKind;
begin
inherited;
- // We pass explicit anonymous methods that use the 'Visitor' argument.
- // This ensures that even if handlers are moved out of context later, they operate on the passed visitor instance.
- SetupHandlers(procedure(Kind: TAstNodeKind; const Handler: TNodeHandler) begin FHandlers[Kind] := Handler; end);
+
+ // 1. Initialize with Sentinel (Fail Fast / Safety)
+ for k := Low(TAstNodeKind) to High(TAstNodeKind) do
+ FHandlers[k] := DefaultSentinel;
+
+ // 2. Allow subclass to overwrite with real handlers
+ SetupHandlers;
end;
procedure TAstVisitor.BeforeDestruction;
@@ -153,129 +160,32 @@ begin
inherited;
end;
-procedure TAstVisitor.SetupHandlers(const Register: TRegisterHandler);
+function TAstVisitor.DefaultSentinel(const Node: IAstNode): T;
begin
- // Map all kinds to the legacy virtual methods using the passed Visitor instance (V).
+ // Controlled crash for missing handlers.
+ raise ENoHandlerException.Create(Node.Kind.ToString);
+end;
- // Core
- Register(akConstant, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitConstant(N.AsConstant); end);
- Register(akIdentifier, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitIdentifier(N.AsIdentifier); end);
- Register(akKeyword, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitKeyword(N.AsKeyword); end);
+procedure TAstVisitor.SetupHandlers;
+begin
+ // Base implementation does nothing.
+ // Subclasses must call Register() here.
+end;
- // Lists
- Register(
- akParameterList,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitParameterList(N.AsParameterList); end
- );
- Register(
- akArgumentList,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitArgumentList(N.AsArgumentList); end
- );
- Register(
- akExpressionList,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitExpressionList(N.AsExpressionList); end
- );
- Register(
- akRecordFieldList,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitRecordFieldList(N.AsRecordFieldList); end
- );
- Register(
- akRecordField,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitRecordField(N.AsRecordField); end
- );
-
- // Structural
- Register(
- akIfExpression,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitIfExpression(N.AsIfExpression); end
- );
- Register(
- akCondExpression,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitCondExpression(N.AsCondExpression); end
- );
- Register(
- akLambdaExpression,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitLambdaExpression(N.AsLambdaExpression); end
- );
- Register(
- akFunctionCall,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitFunctionCall(N.AsFunctionCall); end
- );
- Register(
- akMacroExpansion,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitMacroExpansionNode(N.AsMacroExpansion); end
- );
- Register(
- akBlockExpression,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitBlockExpression(N.AsBlockExpression); end
- );
- Register(
- akVariableDeclaration,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitVariableDeclaration(N.AsVariableDeclaration); end
- );
- Register(akAssignment, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitAssignment(N.AsAssignment); end);
- Register(
- akMacroDefinition,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitMacroDefinition(N.AsMacroDefinition); end
- );
- Register(akQuasiquote, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitQuasiquote(N.AsQuasiquote); end);
- Register(akUnquote, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitUnquote(N.AsUnquote); end);
- Register(
- akUnquoteSplicing,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitUnquoteSplicing(N.AsUnquoteSplicing); end
- );
- Register(akIndexer, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitIndexer(N.AsIndexer); end);
- Register(
- akMemberAccess,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitMemberAccess(N.AsMemberAccess); end
- );
- Register(
- akRecordLiteral,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitRecordLiteral(N.AsRecordLiteral); end
- );
- Register(
- akCreateSeries,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitCreateSeries(N.AsCreateSeries); end
- );
- Register(
- akAddSeriesItem,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitAddSeriesItem(N.AsAddSeriesItem); end
- );
- Register(
- akSeriesLength,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitSeriesLength(N.AsSeriesLength); end
- );
- Register(akRecur, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitRecurNode(N.AsRecur); end);
- Register(akNop, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitNop(N.AsNop); end);
-
- // Pipes
- Register(akPipeInput, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitPipeInput(N.AsPipeInput); end);
- Register(
- akPipeSelectorList,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitPipeSelectorList(N.AsPipeSelectorList); end
- );
- Register(
- akPipeInputList,
- function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitPipeInputList(N.AsPipeInputList); end
- );
- Register(akPipe, function(const V: TAstVisitor; const N: IAstNode): T begin Result := V.VisitPipe(N.AsPipe); end);
+procedure TAstVisitor.Register(Kind: TAstNodeKind; const Handler: TNodeHandler);
+begin
+ if Assigned(Handler) then
+ FHandlers[Kind] := Handler
+ else
+ FHandlers[Kind] := DefaultSentinel;
end;
function TAstVisitor.Visit(const Node: IAstNode): T;
-var
- [unsafe]
- handler: TNodeHandler;
begin
- if not Assigned(Node) then
- Exit(Default(T));
-
- // O(1) Lookup
- handler := TNodeHandler(FHandlers[Node.Kind]);
-
- if Assigned(handler) then
- Result := handler(Self, Node)
+ if Assigned(Node) then
+ Result := FHandlers[Node.Kind](Node)
else
- raise ENoHandlerException.Create(Node.Kind.ToString);
+ Result := Default(T);
end;
function TAstVisitor.Accept(const Node: IAstNode): T;
@@ -283,274 +193,78 @@ begin
Result := Visit(Node);
end;
-// --- Interface Bridge Methods (IAstVisitor -> TDataValue) ---
-
function TAstVisitor.VisitBridge(const Node: IAstNode): TDataValue;
begin
Result := TDataValue.FromGeneric(Visit(Node));
end;
-// --- Default implementations (analogous to original abstract class stubs) ---
-
-function TAstVisitor.VisitConstant(const Node: IConstantNode): T;
-begin
- Result := Default(T);
-end;
-
-function TAstVisitor.VisitIdentifier(const Node: IIdentifierNode): T;
-begin
- Result := Default(T);
-end;
-
-function TAstVisitor.VisitKeyword(const Node: IKeywordNode): T;
-begin
- Result := Default(T);
-end;
-
-function TAstVisitor.VisitParameterList(const Node: IParameterList): T;
-begin
- Result := Default(T);
- for var Item in Node do
- Visit(Item);
-end;
-
-function TAstVisitor.VisitArgumentList(const Node: IArgumentList): T;
-begin
- Result := Default(T);
- for var Item in Node do
- Visit(Item);
-end;
-
-function TAstVisitor.VisitExpressionList(const Node: IExpressionList): T;
-begin
- Result := Default(T);
- for var Item in Node do
- Visit(Item);
-end;
-
-function TAstVisitor.VisitRecordFieldList(const Node: IRecordFieldList): T;
-begin
- Result := Default(T);
- for var Item in Node do
- Visit(Item);
-end;
-
-function TAstVisitor.VisitRecordField(const Node: IRecordFieldNode): T;
-begin
- Result := Default(T);
- Visit(Node.Key);
- Visit(Node.Value);
-end;
-
-function TAstVisitor.VisitIfExpression(const Node: IIfExpressionNode): T;
-begin
- Result := Default(T);
- Visit(Node.Condition);
- Visit(Node.ThenBranch);
- Visit(Node.ElseBranch);
-end;
-
-function TAstVisitor.VisitCondExpression(const Node: ICondExpressionNode): T;
-var
- pair: TCondPair;
-begin
- Result := Default(T);
- for pair in Node.Pairs do
- begin
- Visit(pair.Condition);
- Visit(pair.Branch);
- end;
- Visit(Node.ElseBranch);
-end;
-
-function TAstVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): T;
-begin
- Result := Default(T);
- Visit(Node.Parameters);
- Visit(Node.Body);
-end;
-
-function TAstVisitor.VisitFunctionCall(const Node: IFunctionCallNode): T;
-begin
- Result := Default(T);
- Visit(Node.Callee);
- Visit(Node.Arguments);
-end;
-
-function TAstVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): T;
-begin
- Result := Default(T);
- Visit(Node.CallNode);
- Visit(Node.ExpandedBody);
-end;
-
-function TAstVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): T;
-begin
- Result := Default(T);
- Visit(Node.Expressions);
-end;
-
-function TAstVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): T;
-begin
- Result := Default(T);
- Visit(Node.Target);
- Visit(Node.Initializer);
-end;
-
-function TAstVisitor.VisitAssignment(const Node: IAssignmentNode): T;
-begin
- Result := Default(T);
- Visit(Node.Target);
- Visit(Node.Value);
-end;
-
-function TAstVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): T;
-begin
- Result := Default(T);
- Visit(Node.Name);
- Visit(Node.Parameters);
- Visit(Node.Body);
-end;
-
-function TAstVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): T;
-begin
- Result := Default(T);
- Visit(Node.Expression);
-end;
-
-function TAstVisitor.VisitUnquote(const Node: IUnquoteNode): T;
-begin
- Result := Default(T);
- Visit(Node.Expression);
-end;
-
-function TAstVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): T;
-begin
- Result := Default(T);
- Visit(Node.Expression);
-end;
-
-function TAstVisitor.VisitIndexer(const Node: IIndexerNode): T;
-begin
- Result := Default(T);
- Visit(Node.Base);
- Visit(Node.Index);
-end;
-
-function TAstVisitor.VisitMemberAccess(const Node: IMemberAccessNode): T;
-begin
- Result := Default(T);
- Visit(Node.Base);
- Visit(Node.Member);
-end;
-
-function TAstVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): T;
-begin
- Result := Default(T);
- Visit(Node.Fields);
-end;
-
-function TAstVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): T;
-begin
- Result := Default(T);
-end;
-
-function TAstVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): T;
-begin
- Result := Default(T);
- Visit(Node.Series);
- Visit(Node.Value);
- Visit(Node.Lookback);
-end;
-
-function TAstVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): T;
-begin
- Result := Default(T);
- Visit(Node.Series);
-end;
-
-function TAstVisitor.VisitRecurNode(const Node: IRecurNode): T;
-begin
- Result := Default(T);
- Visit(Node.Arguments);
-end;
-
-function TAstVisitor.VisitNop(const Node: INopNode): T;
-begin
- Result := Default(T);
-end;
-
-function TAstVisitor.VisitPipeInput(const Node: IPipeInputNode): T;
-begin
- Result := Default(T);
- Visit(Node.StreamSource);
- Visit(Node.Selectors);
-end;
-
-function TAstVisitor.VisitPipeSelectorList(const Node: IPipeSelectorList): T;
-begin
- Result := Default(T);
- for var Item in Node do
- Visit(Item);
-end;
-
-function TAstVisitor.VisitPipeInputList(const Node: IPipeInputList): T;
-begin
- Result := Default(T);
- for var Item in Node do
- Visit(Item);
-end;
-
-function TAstVisitor.VisitPipe(const Node: IPipeNode): T;
-begin
- Result := Default(T);
- Visit(Node.Inputs);
- Visit(Node.Transformation);
-end;
-
{ TAstTransformer }
-// --- Base Virtual Implementations ---
-
-function TAstTransformer.VisitConstant(const Node: IConstantNode): IAstNode;
+procedure TAstTransformer.SetupHandlers;
begin
- Result := Node;
+ // Leaves (Identity)
+ Register(akConstant, VisitLeaf);
+ Register(akIdentifier, VisitLeaf);
+ Register(akKeyword, VisitLeaf);
+ Register(akNop, VisitLeaf);
+ Register(akCreateSeries, VisitLeaf);
+
+ // Recursive Structures
+ Register(akParameterList, VisitParameterList);
+ Register(akArgumentList, VisitArgumentList);
+ Register(akExpressionList, VisitExpressionList);
+ Register(akRecordFieldList, VisitRecordFieldList);
+ Register(akRecordField, VisitRecordField);
+
+ Register(akIfExpression, VisitIfExpression);
+ Register(akCondExpression, VisitCondExpression);
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akFunctionCall, VisitFunctionCall);
+ Register(akMacroExpansion, VisitMacroExpansionNode);
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+ Register(akAssignment, VisitAssignment);
+ Register(akMacroDefinition, VisitMacroDefinition);
+
+ Register(akQuasiquote, VisitQuasiquote);
+ Register(akUnquote, VisitUnquote);
+ Register(akUnquoteSplicing, VisitUnquoteSplicing);
+
+ Register(akIndexer, VisitIndexer);
+ Register(akMemberAccess, VisitMemberAccess);
+ Register(akRecordLiteral, VisitRecordLiteral);
+ Register(akAddSeriesItem, VisitAddSeriesItem);
+ Register(akSeriesLength, VisitSeriesLength);
+ Register(akRecur, VisitRecurNode);
+
+ Register(akPipeInput, VisitPipeInput);
+ Register(akPipeSelectorList, VisitPipeSelectorList);
+ Register(akPipeInputList, VisitPipeInputList);
+ Register(akPipe, VisitPipe);
end;
-function TAstTransformer.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
+function TAstTransformer.VisitLeaf(const N: IAstNode): IAstNode;
begin
- Result := Node;
+ Result := N;
end;
-function TAstTransformer.VisitKeyword(const Node: IKeywordNode): IAstNode;
-begin
- Result := Node;
-end;
+// --- Lists ---
-function TAstTransformer.VisitNop(const Node: INopNode): IAstNode;
-begin
- Result := Node;
-end;
-
-function TAstTransformer.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
-begin
- Result := Node;
-end;
-
-// --- List Transformations ---
-
-function TAstTransformer.VisitParameterList(const Node: IParameterList): IAstNode;
+function TAstTransformer.VisitParameterList(const N: IAstNode): IAstNode;
var
+ L: IParameterList;
hasChanged: Boolean;
newItems: TArray;
item, newItem: IIdentifierNode;
i: Integer;
begin
+ L := N.AsParameterList;
hasChanged := False;
- SetLength(newItems, Node.Count);
- for i := 0 to Node.Count - 1 do
+ SetLength(newItems, L.Count);
+ for i := 0 to L.Count - 1 do
begin
- item := Node[i];
+ item := L[i];
+ // We must cast the result back to IIdentifierNode.
newItem := Visit(item).AsIdentifier;
newItems[i] := newItem;
if item <> newItem then
@@ -558,24 +272,25 @@ begin
end;
if not hasChanged then
- Result := Node
+ Result := N
else
- // Manually reconstruct using concrete class to verify transformation
- Result := TParameterList.Create(newItems, Node.Identity);
+ Result := TParameterList.Create(newItems, N.Identity);
end;
-function TAstTransformer.VisitArgumentList(const Node: IArgumentList): IAstNode;
+function TAstTransformer.VisitArgumentList(const N: IAstNode): IAstNode;
var
+ L: IArgumentList;
hasChanged: Boolean;
newItems: TArray;
item, newItem: IAstNode;
i: Integer;
begin
+ L := N.AsArgumentList;
hasChanged := False;
- SetLength(newItems, Node.Count);
- for i := 0 to Node.Count - 1 do
+ SetLength(newItems, L.Count);
+ for i := 0 to L.Count - 1 do
begin
- item := Node[i];
+ item := L[i];
newItem := Visit(item);
newItems[i] := newItem;
if item <> newItem then
@@ -583,23 +298,25 @@ begin
end;
if not hasChanged then
- Result := Node
+ Result := N
else
- Result := TArgumentList.Create(newItems, Node.Identity);
+ Result := TArgumentList.Create(newItems, N.Identity);
end;
-function TAstTransformer.VisitExpressionList(const Node: IExpressionList): IAstNode;
+function TAstTransformer.VisitExpressionList(const N: IAstNode): IAstNode;
var
+ L: IExpressionList;
hasChanged: Boolean;
newItems: TArray;
item, newItem: IAstNode;
i: Integer;
begin
+ L := N.AsExpressionList;
hasChanged := False;
- SetLength(newItems, Node.Count);
- for i := 0 to Node.Count - 1 do
+ SetLength(newItems, L.Count);
+ for i := 0 to L.Count - 1 do
begin
- item := Node[i];
+ item := L[i];
newItem := Visit(item);
newItems[i] := newItem;
if item <> newItem then
@@ -607,24 +324,25 @@ begin
end;
if not hasChanged then
- Result := Node
+ Result := N
else
- Result := TExpressionList.Create(newItems, Node.Identity);
+ Result := TExpressionList.Create(newItems, N.Identity);
end;
-function TAstTransformer.VisitRecordFieldList(const Node: IRecordFieldList): IAstNode;
+function TAstTransformer.VisitRecordFieldList(const N: IAstNode): IAstNode;
var
+ L: IRecordFieldList;
hasChanged: Boolean;
newItems: TArray;
item, newItem: IRecordFieldNode;
i: Integer;
begin
+ L := N.AsRecordFieldList;
hasChanged := False;
- SetLength(newItems, Node.Count);
- for i := 0 to Node.Count - 1 do
+ SetLength(newItems, L.Count);
+ for i := 0 to L.Count - 1 do
begin
- item := Node[i];
- // Ensure we transform the field node itself via Visit to trigger VisitRecordField
+ item := L[i];
newItem := Visit(item).AsRecordField;
newItems[i] := newItem;
if item <> newItem then
@@ -632,322 +350,320 @@ begin
end;
if not hasChanged then
- Result := Node
+ Result := N
else
- Result := TRecordFieldList.Create(newItems, Node.Identity);
+ Result := TRecordFieldList.Create(newItems, N.Identity);
end;
-function TAstTransformer.VisitRecordField(const Node: IRecordFieldNode): IAstNode;
+function TAstTransformer.VisitRecordField(const N: IAstNode): IAstNode;
var
+ F: IRecordFieldNode;
newKey: IKeywordNode;
newValue: IAstNode;
begin
- newKey := Visit(Node.Key).AsKeyword;
- newValue := Visit(Node.Value);
+ F := N.AsRecordField;
+ newKey := Visit(F.Key).AsKeyword;
+ newValue := Visit(F.Value);
- if (newKey = Node.Key) and (newValue = Node.Value) then
- Result := Node
+ if (newKey = F.Key) and (newValue = F.Value) then
+ Result := N
else
- Result := TAst.RecordField(newKey, newValue, Node.Identity.Location);
+ Result := TAst.RecordField(N.Identity, newKey, newValue);
end;
-// --- Node Transformations ---
+// --- Structures ---
-function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): IAstNode;
+function TAstTransformer.VisitIfExpression(const N: IAstNode): IAstNode;
var
- newCond, newThen, newElse: IAstNode;
+ E: IIfExpressionNode;
+ c, t, eBr: IAstNode;
begin
- newCond := Visit(Node.Condition);
- newThen := Visit(Node.ThenBranch);
- newElse := Visit(Node.ElseBranch);
+ E := N.AsIfExpression;
+ c := Visit(E.Condition);
+ t := Visit(E.ThenBranch);
+ eBr := Visit(E.ElseBranch);
- if (newCond = Node.Condition) and (newThen = Node.ThenBranch) and (newElse = Node.ElseBranch) then
- Result := Node
+ if (c = E.Condition) and (t = E.ThenBranch) and (eBr = E.ElseBranch) then
+ Result := N
else
- Result := TAst.IfExpr(Node.Identity, newCond, newThen, newElse, Node.StaticType);
+ Result := TAst.IfExpr(N.Identity, c, t, eBr, E.StaticType);
end;
-function TAstTransformer.VisitCondExpression(const Node: ICondExpressionNode): IAstNode;
+function TAstTransformer.VisitCondExpression(const N: IAstNode): IAstNode;
var
- newPairs: TArray;
- newElse: IAstNode;
+ E: ICondExpressionNode;
hasChanged: Boolean;
+ newPairs: TArray;
+ newElse, c, b: IAstNode;
i: Integer;
- newCond, newBranch: IAstNode;
begin
+ E := N.AsCondExpression;
hasChanged := False;
- SetLength(newPairs, Length(Node.Pairs));
-
- for i := 0 to High(Node.Pairs) do
+ SetLength(newPairs, Length(E.Pairs));
+ for i := 0 to High(E.Pairs) do
begin
- newCond := Visit(Node.Pairs[i].Condition);
- newBranch := Visit(Node.Pairs[i].Branch);
- newPairs[i] := TCondPair.Create(newCond, newBranch);
-
- if (newCond <> Node.Pairs[i].Condition) or (newBranch <> Node.Pairs[i].Branch) then
+ c := Visit(E.Pairs[i].Condition);
+ b := Visit(E.Pairs[i].Branch);
+ newPairs[i] := TCondPair.Create(c, b);
+ if (c <> E.Pairs[i].Condition) or (b <> E.Pairs[i].Branch) then
hasChanged := True;
end;
- newElse := Visit(Node.ElseBranch); // Visit handles nil
- if newElse <> Node.ElseBranch then
+ newElse := Visit(E.ElseBranch);
+ if newElse <> E.ElseBranch then
hasChanged := True;
if not hasChanged then
- Result := Node
+ Result := N
else
- Result := TAst.CondExpr(Node.Identity, newPairs, newElse, Node.StaticType);
+ Result := TAst.CondExpr(N.Identity, newPairs, newElse, E.StaticType);
end;
-function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
+function TAstTransformer.VisitLambdaExpression(const N: IAstNode): IAstNode;
var
- newParams: IParameterList;
- newBody: IAstNode;
+ E: ILambdaExpressionNode;
+ p: IParameterList;
+ b: IAstNode;
begin
- newParams := Visit(Node.Parameters).AsParameterList;
- newBody := Visit(Node.Body);
+ E := N.AsLambdaExpression;
+ p := Visit(E.Parameters).AsParameterList;
+ b := Visit(E.Body);
- if (newParams = Node.Parameters) and (newBody = Node.Body) then
- Result := Node
+ if (p = E.Parameters) and (b = E.Body) then
+ Result := N
else
- begin
- // Rebuild Lambda via concrete constructor to keep Layout/Descriptor info
- // TAst factory would reset layout info.
+ // Reconstruct with full metadata
Result :=
- TLambdaExpressionNode.Create(
- newParams,
- newBody,
- Node.StaticType,
- Node.Layout,
- Node.Descriptor,
- Node.Upvalues,
- Node.HasNestedLambdas,
- Node.IsPure,
- Node.Identity
- );
- end;
+ TLambdaExpressionNode.Create(p, b, E.StaticType, E.Layout, E.Descriptor, E.Upvalues, E.HasNestedLambdas, E.IsPure, N.Identity);
end;
-function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
+function TAstTransformer.VisitFunctionCall(const N: IAstNode): IAstNode;
var
- newCallee: IAstNode;
- newArgs: IArgumentList;
+ C: IFunctionCallNode;
+ callee: IAstNode;
+ args: IArgumentList;
begin
- newCallee := Visit(Node.Callee);
- newArgs := Visit(Node.Arguments).AsArgumentList;
+ C := N.AsFunctionCall;
+ callee := Visit(C.Callee);
+ args := Visit(C.Arguments).AsArgumentList;
- if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then
- Result := Node
+ if (callee = C.Callee) and (args = C.Arguments) then
+ Result := N
else
- begin
- Result :=
- TFunctionCallNode
- .Create(newCallee, newArgs, Node.StaticType, Node.IsTailCall, Node.StaticTarget, Node.IsTargetPure, Node.Identity);
- end;
+ Result := TAst.FunctionCall(N.Identity, callee, args, C.StaticType, C.IsTailCall, C.StaticTarget, C.IsTargetPure);
end;
-function TAstTransformer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): IAstNode;
+function TAstTransformer.VisitMacroExpansionNode(const N: IAstNode): IAstNode;
var
- newBody: IAstNode;
+ M: IMacroExpansionNode;
+ exp: IAstNode;
begin
- newBody := Visit(Node.ExpandedBody);
- if newBody = Node.ExpandedBody then
- exit(Node);
-
- // Macro Expansion Nodes are structural, reuse identity.
- // Note: We don't visit the CallNode as it is the "source" which doesn't change during this transformation typically
- Result := TAst.MacroExpansionNode(Node.Identity, Node.CallNode, newBody);
-end;
-
-function TAstTransformer.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
-var
- newExprs: IExpressionList;
-begin
- newExprs := Visit(Node.Expressions).AsExpressionList;
-
- if newExprs = Node.Expressions then
- Result := Node
+ M := N.AsMacroExpansion;
+ exp := Visit(M.ExpandedBody);
+ if exp = M.ExpandedBody then
+ Result := N
else
- // Rebuild manually to preserve type
- Result := TBlockExpressionNode.Create(newExprs, Node.StaticType, Node.Identity);
+ Result := TAst.MacroExpansionNode(N.Identity, M.CallNode, exp);
end;
-function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
+function TAstTransformer.VisitBlockExpression(const N: IAstNode): IAstNode;
var
- newTarget: IAstNode;
- newInit: IAstNode;
+ B: IBlockExpressionNode;
+ e: IExpressionList;
begin
- newTarget := Visit(Node.Target);
- newInit := Visit(Node.Initializer);
-
- if (newTarget = Node.Target) and (newInit = Node.Initializer) then
- Result := Node
+ B := N.AsBlockExpression;
+ e := Visit(B.Expressions).AsExpressionList;
+ if e = B.Expressions then
+ Result := N
else
- begin
- Result := TAst.VarDecl(Node.Identity, newTarget, newInit, Node.StaticType, Node.IsBoxed);
- end;
+ Result := TAst.Block(N.Identity, e, B.StaticType);
end;
-function TAstTransformer.VisitAssignment(const Node: IAssignmentNode): IAstNode;
+function TAstTransformer.VisitVariableDeclaration(const N: IAstNode): IAstNode;
var
- newTarget: IAstNode;
- newValue: IAstNode;
+ V: IVariableDeclarationNode;
+ t, i: IAstNode;
begin
- newTarget := Visit(Node.Target);
- newValue := Visit(Node.Value);
-
- if (newTarget = Node.Target) and (newValue = Node.Value) then
- Result := Node
+ V := N.AsVariableDeclaration;
+ t := Visit(V.Target);
+ i := Visit(V.Initializer);
+ if (t = V.Target) and (i = V.Initializer) then
+ Result := N
else
- Result := TAst.Assign(Node.Identity, newTarget, newValue, Node.StaticType);
+ Result := TAst.VarDecl(N.Identity, t, i, V.StaticType, V.IsBoxed);
end;
-function TAstTransformer.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
-begin
- // Macro definitions are usually stripped/ignored in transformers,
- // but we return them as-is to support partial pipelines.
- Result := Node;
-end;
-
-function TAstTransformer.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode;
+function TAstTransformer.VisitAssignment(const N: IAstNode): IAstNode;
var
- newExpr: IAstNode;
+ A: IAssignmentNode;
+ t, v: IAstNode;
begin
- newExpr := Visit(Node.Expression);
- if newExpr = Node.Expression then
- Result := Node
+ A := N.AsAssignment;
+ t := Visit(A.Target);
+ v := Visit(A.Value);
+ if (t = A.Target) and (v = A.Value) then
+ Result := N
else
- Result := TAst.Quasiquote(Node.Identity, newExpr);
+ Result := TAst.Assign(N.Identity, t, v, A.StaticType);
end;
-function TAstTransformer.VisitUnquote(const Node: IUnquoteNode): IAstNode;
-var
- newExpr: IAstNode;
+function TAstTransformer.VisitMacroDefinition(const N: IAstNode): IAstNode;
begin
- newExpr := Visit(Node.Expression);
- if newExpr = Node.Expression then
- Result := Node
- else
- Result := TAst.Unquote(Node.Identity, newExpr);
+ // Macro definitions are typically constant during runtime transformation, return as is.
+ Result := N;
end;
-function TAstTransformer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
+function TAstTransformer.VisitQuasiquote(const N: IAstNode): IAstNode;
var
- newExpr: IAstNode;
+ Q: IQuasiquoteNode;
+ e: IAstNode;
begin
- newExpr := Visit(Node.Expression);
-
- if (newExpr = Node.Expression) then
- Result := Node
+ Q := N.AsQuasiquote;
+ e := Visit(Q.Expression);
+ if e = Q.Expression then
+ Result := N
else
- Result := TAst.UnquoteSplicing(Node.Identity, newExpr.AsQuasiquote);
+ Result := TAst.Quasiquote(N.Identity, e);
end;
-function TAstTransformer.VisitIndexer(const Node: IIndexerNode): IAstNode;
+function TAstTransformer.VisitUnquote(const N: IAstNode): IAstNode;
var
- newBase, newIndex: IAstNode;
+ U: IUnquoteNode;
+ e: IAstNode;
begin
- newBase := Visit(Node.Base);
- newIndex := Visit(Node.Index);
-
- if (newBase = Node.Base) and (newIndex = Node.Index) then
- Result := Node
+ U := N.AsUnquote;
+ e := Visit(U.Expression);
+ if e = U.Expression then
+ Result := N
else
- Result := TAst.Indexer(Node.Identity, newBase, newIndex, Node.StaticType);
+ Result := TAst.Unquote(N.Identity, e);
end;
-function TAstTransformer.VisitMemberAccess(const Node: IMemberAccessNode): IAstNode;
+function TAstTransformer.VisitUnquoteSplicing(const N: IAstNode): IAstNode;
var
- newBase: IAstNode;
- newMember: IKeywordNode;
+ U: IUnquoteSplicingNode;
+ e: IAstNode;
begin
- newBase := Visit(Node.Base);
- newMember := Visit(Node.Member).AsKeyword;
-
- if (newBase = Node.Base) and (newMember = Node.Member) then
- Result := Node
+ U := N.AsUnquoteSplicing;
+ e := Visit(U.Expression);
+ if e = U.Expression then
+ Result := N
else
- Result := TAst.MemberAccess(Node.Identity, newBase, newMember, Node.StaticType);
+ Result := TAst.UnquoteSplicing(N.Identity, e.AsQuasiquote);
end;
-function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
+function TAstTransformer.VisitIndexer(const N: IAstNode): IAstNode;
var
- newFields: IRecordFieldList;
+ I: IIndexerNode;
+ b, idx: IAstNode;
begin
- newFields := Visit(Node.Fields).AsRecordFieldList;
-
- if newFields = Node.Fields then
- Result := Node
+ I := N.AsIndexer;
+ b := Visit(I.Base);
+ idx := Visit(I.Index);
+ if (b = I.Base) and (idx = I.Index) then
+ Result := N
else
- begin
- Result := TRecordLiteralNode.Create(newFields, Node.ScalarDefinition, Node.GenericDefinition, Node.StaticType, Node.Identity);
- end;
+ Result := TAst.Indexer(N.Identity, b, idx, I.StaticType);
end;
-function TAstTransformer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): IAstNode;
+function TAstTransformer.VisitMemberAccess(const N: IAstNode): IAstNode;
var
- newSeries: IIdentifierNode;
- newValue, newLookback: IAstNode;
+ M: IMemberAccessNode;
+ b: IAstNode;
begin
- newSeries := Visit(Node.Series).AsIdentifier;
- newValue := Visit(Node.Value);
- newLookback := Visit(Node.Lookback);
-
- if (newSeries = Node.Series) and (newValue = Node.Value) and (newLookback = Node.Lookback) then
- Result := Node
+ M := N.AsMemberAccess;
+ b := Visit(M.Base);
+ if b = M.Base then
+ Result := N
else
- Result := TAst.AddSeriesItem(Node.Identity, newSeries, newValue, newLookback, Node.StaticType);
+ Result := TAst.MemberAccess(N.Identity, b, M.Member, M.StaticType);
end;
-function TAstTransformer.VisitSeriesLength(const Node: ISeriesLengthNode): IAstNode;
+function TAstTransformer.VisitRecordLiteral(const N: IAstNode): IAstNode;
var
- newSeries: IIdentifierNode;
+ R: IRecordLiteralNode;
+ f: IRecordFieldList;
begin
- newSeries := Visit(Node.Series).AsIdentifier;
-
- if newSeries = Node.Series then
- Result := Node
+ R := N.AsRecordLiteral;
+ f := Visit(R.Fields).AsRecordFieldList;
+ if f = R.Fields then
+ Result := N
else
- Result := TAst.SeriesLength(Node.Identity, newSeries, Node.StaticType);
+ Result := TAst.RecordLiteral(N.Identity, f, R.ScalarDefinition, R.GenericDefinition, R.StaticType);
end;
-function TAstTransformer.VisitRecurNode(const Node: IRecurNode): IAstNode;
+function TAstTransformer.VisitAddSeriesItem(const N: IAstNode): IAstNode;
var
- newArgs: IArgumentList;
+ A: IAddSeriesItemNode;
+ s, v, l: IAstNode;
begin
- newArgs := Visit(Node.Arguments).AsArgumentList;
+ A := N.AsAddSeriesItem;
+ s := Visit(A.Series);
+ v := Visit(A.Value);
+ l := Visit(A.Lookback);
- if newArgs = Node.Arguments then
- Result := Node
+ if (s = A.Series) and (v = A.Value) and (l = A.Lookback) then
+ Result := N
else
- Result := TRecurNode.Create(newArgs, Node.StaticType, Node.Identity);
+ Result := TAst.AddSeriesItem(N.Identity, s.AsIdentifier, v, l, A.StaticType);
end;
-function TAstTransformer.VisitPipeInput(const Node: IPipeInputNode): IAstNode;
+function TAstTransformer.VisitSeriesLength(const N: IAstNode): IAstNode;
var
- newSource: IIdentifierNode;
- newSels: IPipeSelectorList;
+ S: ISeriesLengthNode;
+ series: IAstNode;
begin
- newSource := Visit(Node.StreamSource).AsIdentifier;
- newSels := Visit(Node.Selectors).AsPipeSelectorList;
-
- if (newSource = Node.StreamSource) and (newSels = Node.Selectors) then
- Result := Node
+ S := N.AsSeriesLength;
+ series := Visit(S.Series);
+ if series = S.Series then
+ Result := N
else
- Result := TAst.PipeInput(newSource, newSels, Node.Identity.Location);
+ Result := TAst.SeriesLength(N.Identity, series.AsIdentifier, S.StaticType);
end;
-function TAstTransformer.VisitPipeSelectorList(const Node: IPipeSelectorList): IAstNode;
+function TAstTransformer.VisitRecurNode(const N: IAstNode): IAstNode;
var
+ R: IRecurNode;
+ a: IArgumentList;
+begin
+ R := N.AsRecur;
+ a := Visit(R.Arguments).AsArgumentList;
+ if a = R.Arguments then
+ Result := N
+ else
+ Result := TAst.Recur(N.Identity, a, R.StaticType);
+end;
+
+function TAstTransformer.VisitPipeInput(const N: IAstNode): IAstNode;
+var
+ P: IPipeInputNode;
+ s: IAstNode;
+ sel: IPipeSelectorList;
+begin
+ P := N.AsPipeInput;
+ s := Visit(P.StreamSource);
+ sel := Visit(P.Selectors).AsPipeSelectorList;
+
+ if (s = P.StreamSource) and (sel = P.Selectors) then
+ Result := N
+ else
+ Result := TAst.PipeInput(s.AsIdentifier, sel, N.Identity.Location);
+end;
+
+function TAstTransformer.VisitPipeSelectorList(const N: IAstNode): IAstNode;
+var
+ L: IPipeSelectorList;
hasChanged: Boolean;
newItems: TArray;
item, newItem: IKeywordNode;
i: Integer;
begin
+ L := N.AsPipeSelectorList;
hasChanged := False;
- SetLength(newItems, Node.Count);
- for i := 0 to Node.Count - 1 do
+ SetLength(newItems, L.Count);
+ for i := 0 to L.Count - 1 do
begin
- item := Node[i];
+ item := L[i];
newItem := Visit(item).AsKeyword;
newItems[i] := newItem;
if item <> newItem then
@@ -955,50 +671,272 @@ begin
end;
if not hasChanged then
- Result := Node
+ Result := N
else
- // Manually reconstruct using helper
- Result := TAst.PipeSelectorList(newItems, Node.Identity.Location);
+ Result := TAst.PipeSelectorList(newItems, N.Identity.Location);
end;
-function TAstTransformer.VisitPipeInputList(const Node: IPipeInputList): IAstNode;
+function TAstTransformer.VisitPipeInputList(const N: IAstNode): IAstNode;
var
+ L: IPipeInputList;
hasChanged: Boolean;
newItems: TArray;
item, newItem: IPipeInputNode;
i: Integer;
begin
+ L := N.AsPipeInputList;
hasChanged := False;
- SetLength(newItems, Node.Count);
- for i := 0 to Node.Count - 1 do
+ SetLength(newItems, L.Count);
+ for i := 0 to L.Count - 1 do
begin
- item := Node[i];
- newItem := Visit(item).AsPipeInput; // Explicit cast via new helper
+ item := L[i];
+ newItem := Visit(item).AsPipeInput;
newItems[i] := newItem;
if item <> newItem then
hasChanged := True;
end;
if not hasChanged then
- Result := Node
+ Result := N
else
- // Using TList implementation directly implies we are creating the list structure
- // Since IPipeInputList is just INodeList, we reuse TPipeInputList
- Result := TPipeInputList.Create(newItems, Node.Identity);
+ Result := TPipeInputList.Create(newItems, N.Identity);
end;
-function TAstTransformer.VisitPipe(const Node: IPipeNode): IAstNode;
+function TAstTransformer.VisitPipe(const N: IAstNode): IAstNode;
var
- newInputs: IPipeInputList;
- newTrans: ILambdaExpressionNode;
+ P: IPipeNode;
+ inp: IPipeInputList;
+ trans: ILambdaExpressionNode;
begin
- newInputs := Visit(Node.Inputs).AsPipeInputList;
- newTrans := Visit(Node.Transformation).AsLambdaExpression;
+ P := N.AsPipe;
+ inp := Visit(P.Inputs).AsPipeInputList;
+ trans := Visit(P.Transformation).AsLambdaExpression;
- if (newInputs = Node.Inputs) and (newTrans = Node.Transformation) then
- Result := Node
+ if (inp = P.Inputs) and (trans = P.Transformation) then
+ Result := N
else
- Result := TAst.Pipe(Node.Identity, newInputs, newTrans, Node.StaticType);
+ Result := TAst.Pipe(N.Identity, inp, trans, P.StaticType);
+end;
+
+{ TAstVisitor (Walker) }
+
+procedure TAstVisitor.SetupHandlers;
+begin
+ // Leaves (Do nothing)
+ Register(akConstant, WalkLeaf);
+ Register(akIdentifier, WalkLeaf);
+ Register(akKeyword, WalkLeaf);
+ Register(akNop, WalkLeaf);
+ Register(akCreateSeries, WalkLeaf);
+
+ // Recursive Structures
+ Register(akParameterList, WalkParameterList);
+ Register(akArgumentList, WalkArgumentList);
+ Register(akExpressionList, WalkExpressionList);
+ Register(akRecordFieldList, WalkRecordFieldList);
+ Register(akRecordField, WalkRecordField);
+
+ Register(akIfExpression, WalkIfExpression);
+ Register(akCondExpression, WalkCondExpression);
+ Register(akLambdaExpression, WalkLambdaExpression);
+ Register(akFunctionCall, WalkFunctionCall);
+ Register(akMacroExpansion, WalkMacroExpansionNode);
+ Register(akBlockExpression, WalkBlockExpression);
+ Register(akVariableDeclaration, WalkVariableDeclaration);
+ Register(akAssignment, WalkAssignment);
+ Register(akMacroDefinition, WalkMacroDefinition);
+
+ // Wrappers (Generic Node Handler)
+ Register(akQuasiquote, WalkWrapper);
+ Register(akUnquote, WalkWrapper);
+ Register(akUnquoteSplicing, WalkWrapper);
+
+ Register(akIndexer, WalkIndexer);
+ Register(akMemberAccess, WalkMemberAccess);
+ Register(akRecordLiteral, WalkRecordLiteral);
+ Register(akAddSeriesItem, WalkAddSeriesItem);
+ Register(akSeriesLength, WalkSeriesLength);
+ Register(akRecur, WalkRecurNode);
+
+ // Pipes
+ Register(akPipeInput, WalkPipeInput);
+ Register(akPipeSelectorList, WalkPipeSelectorList);
+ Register(akPipeInputList, WalkPipeInputList);
+ Register(akPipe, WalkPipe);
+end;
+
+function TAstVisitor.WalkLeaf(const N: IAstNode): TVoid;
+begin
+ // Do nothing
+end;
+
+function TAstVisitor.WalkWrapper(const N: IAstNode): TVoid;
+begin
+ if N.Kind = akQuasiquote then
+ Visit(N.AsQuasiquote.Expression)
+ else if N.Kind = akUnquote then
+ Visit(N.AsUnquote.Expression)
+ else if N.Kind = akUnquoteSplicing then
+ Visit(N.AsUnquoteSplicing.Expression);
+end;
+
+function TAstVisitor.WalkParameterList(const N: IAstNode): TVoid;
+begin
+ for var item in N.AsParameterList do
+ Visit(item);
+end;
+
+function TAstVisitor.WalkArgumentList(const N: IAstNode): TVoid;
+begin
+ for var item in N.AsArgumentList do
+ Visit(item);
+end;
+
+function TAstVisitor.WalkExpressionList(const N: IAstNode): TVoid;
+begin
+ for var item in N.AsExpressionList do
+ Visit(item);
+end;
+
+function TAstVisitor.WalkRecordFieldList(const N: IAstNode): TVoid;
+begin
+ for var item in N.AsRecordFieldList do
+ Visit(item);
+end;
+
+function TAstVisitor.WalkRecordField(const N: IAstNode): TVoid;
+begin
+ var F := N.AsRecordField;
+ Visit(F.Key);
+ Visit(F.Value);
+end;
+
+function TAstVisitor.WalkIfExpression(const N: IAstNode): TVoid;
+begin
+ var E := N.AsIfExpression;
+ Visit(E.Condition);
+ Visit(E.ThenBranch);
+ Visit(E.ElseBranch);
+end;
+
+function TAstVisitor.WalkCondExpression(const N: IAstNode): TVoid;
+begin
+ var E := N.AsCondExpression;
+ for var pair in E.Pairs do
+ begin
+ Visit(pair.Condition);
+ Visit(pair.Branch);
+ end;
+ Visit(E.ElseBranch);
+end;
+
+function TAstVisitor.WalkLambdaExpression(const N: IAstNode): TVoid;
+begin
+ var E := N.AsLambdaExpression;
+ Visit(E.Parameters);
+ Visit(E.Body);
+end;
+
+function TAstVisitor.WalkFunctionCall(const N: IAstNode): TVoid;
+begin
+ var C := N.AsFunctionCall;
+ Visit(C.Callee);
+ Visit(C.Arguments);
+end;
+
+function TAstVisitor.WalkMacroExpansionNode(const N: IAstNode): TVoid;
+begin
+ Visit(N.AsMacroExpansion.ExpandedBody);
+end;
+
+function TAstVisitor.WalkBlockExpression(const N: IAstNode): TVoid;
+begin
+ Visit(N.AsBlockExpression.Expressions);
+end;
+
+function TAstVisitor.WalkVariableDeclaration(const N: IAstNode): TVoid;
+begin
+ var V := N.AsVariableDeclaration;
+ Visit(V.Target);
+ Visit(V.Initializer);
+end;
+
+function TAstVisitor.WalkAssignment(const N: IAstNode): TVoid;
+begin
+ var A := N.AsAssignment;
+ Visit(A.Target);
+ Visit(A.Value);
+end;
+
+function TAstVisitor.WalkMacroDefinition(const N: IAstNode): TVoid;
+begin
+ var M := N.AsMacroDefinition;
+ Visit(M.Name);
+ Visit(M.Parameters);
+ Visit(M.Body);
+end;
+
+function TAstVisitor.WalkIndexer(const N: IAstNode): TVoid;
+begin
+ var I := N.AsIndexer;
+ Visit(I.Base);
+ Visit(I.Index);
+end;
+
+function TAstVisitor.WalkMemberAccess(const N: IAstNode): TVoid;
+begin
+ var M := N.AsMemberAccess;
+ Visit(M.Base);
+ Visit(M.Member);
+end;
+
+function TAstVisitor.WalkRecordLiteral(const N: IAstNode): TVoid;
+begin
+ Visit(N.AsRecordLiteral.Fields);
+end;
+
+function TAstVisitor.WalkAddSeriesItem(const N: IAstNode): TVoid;
+begin
+ var A := N.AsAddSeriesItem;
+ Visit(A.Series);
+ Visit(A.Value);
+ Visit(A.Lookback);
+end;
+
+function TAstVisitor.WalkSeriesLength(const N: IAstNode): TVoid;
+begin
+ Visit(N.AsSeriesLength.Series);
+end;
+
+function TAstVisitor.WalkRecurNode(const N: IAstNode): TVoid;
+begin
+ Visit(N.AsRecur.Arguments);
+end;
+
+function TAstVisitor.WalkPipeInput(const N: IAstNode): TVoid;
+begin
+ var P := N.AsPipeInput;
+ Visit(P.StreamSource);
+ Visit(P.Selectors);
+end;
+
+function TAstVisitor.WalkPipeSelectorList(const N: IAstNode): TVoid;
+begin
+ for var item in N.AsPipeSelectorList do
+ Visit(item);
+end;
+
+function TAstVisitor.WalkPipeInputList(const N: IAstNode): TVoid;
+begin
+ for var item in N.AsPipeInputList do
+ Visit(item);
+end;
+
+function TAstVisitor.WalkPipe(const N: IAstNode): TVoid;
+begin
+ var P := N.AsPipe;
+ Visit(P.Inputs);
+ Visit(P.Transformation);
end;
end.
diff --git a/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas b/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas
index 14893c4..5329d9b 100644
--- a/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas
+++ b/Src/AST/Myc.Fmx.AstEditor.Visualizer.pas
@@ -15,10 +15,11 @@ uses
type
// Implementation of the Factory/Visitor for the Virtual Tree
+ // Uses TAstVisitor which now uses closures for dispatch.
TAstVisualizer = class(TAstVisitor, IAstVisualizer)
private
FExprDepth: Integer;
- FParentNode: TVisualNode; // Changed from TControl
+ FParentNode: TVisualNode;
FWorkspace: TWorkspace;
function CallAccept(const Node: IAstNode): TAstViewNode;
@@ -29,49 +30,58 @@ type
function GetWorkspace: TWorkspace;
protected
- // --- Visitor Implementations ---
- function VisitConstant(const Node: IConstantNode): TAstViewNode; override;
- function VisitIdentifier(const Node: IIdentifierNode): TAstViewNode; override;
- function VisitKeyword(const Node: IKeywordNode): TAstViewNode; override;
+ // Setup registry using closures
+ procedure SetupHandlers; override;
+
+ // --- Visitor Implementations (Strict IAstNode signature) ---
+ function VisitConstant(const Node: IAstNode): TAstViewNode;
+ function VisitIdentifier(const Node: IAstNode): TAstViewNode;
+ function VisitKeyword(const Node: IAstNode): TAstViewNode;
// Lists
- function VisitParameterList(const Node: IParameterList): TAstViewNode; override;
- function VisitArgumentList(const Node: IArgumentList): TAstViewNode; override;
- function VisitExpressionList(const Node: IExpressionList): TAstViewNode; override;
- function VisitRecordFieldList(const Node: IRecordFieldList): TAstViewNode; override;
- function VisitRecordField(const Node: IRecordFieldNode): TAstViewNode; override;
+ function VisitParameterList(const Node: IAstNode): TAstViewNode;
+ function VisitArgumentList(const Node: IAstNode): TAstViewNode;
+ function VisitExpressionList(const Node: IAstNode): TAstViewNode;
+ function VisitRecordFieldList(const Node: IAstNode): TAstViewNode;
+ function VisitRecordField(const Node: IAstNode): TAstViewNode;
// Control Flow
- function VisitIfExpression(const Node: IIfExpressionNode): TAstViewNode; override;
- function VisitCondExpression(const Node: ICondExpressionNode): TAstViewNode; override; // Replaces Ternary
- function VisitBlockExpression(const Node: IBlockExpressionNode): TAstViewNode; override;
- function VisitRecurNode(const Node: IRecurNode): TAstViewNode; override;
- function VisitNop(const Node: INopNode): TAstViewNode; override;
+ function VisitIfExpression(const Node: IAstNode): TAstViewNode;
+ function VisitCondExpression(const Node: IAstNode): TAstViewNode;
+ function VisitBlockExpression(const Node: IAstNode): TAstViewNode;
+ function VisitRecurNode(const Node: IAstNode): TAstViewNode;
+ function VisitNop(const Node: IAstNode): TAstViewNode;
// Functions
- function VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstViewNode; override;
- function VisitFunctionCall(const Node: IFunctionCallNode): TAstViewNode; override;
- function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TAstViewNode; override;
+ function VisitLambdaExpression(const Node: IAstNode): TAstViewNode;
+ function VisitFunctionCall(const Node: IAstNode): TAstViewNode;
+ function VisitMacroExpansionNode(const Node: IAstNode): TAstViewNode;
// Declarations
- function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstViewNode; override;
- function VisitAssignment(const Node: IAssignmentNode): TAstViewNode; override;
- function VisitMacroDefinition(const Node: IMacroDefinitionNode): TAstViewNode; override;
+ function VisitVariableDeclaration(const Node: IAstNode): TAstViewNode;
+ function VisitAssignment(const Node: IAstNode): TAstViewNode;
+ function VisitMacroDefinition(const Node: IAstNode): TAstViewNode;
// Metaprogramming
- function VisitQuasiquote(const Node: IQuasiquoteNode): TAstViewNode; override;
- function VisitUnquote(const Node: IUnquoteNode): TAstViewNode; override;
- function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TAstViewNode; override;
+ function VisitQuasiquote(const Node: IAstNode): TAstViewNode;
+ function VisitUnquote(const Node: IAstNode): TAstViewNode;
+ function VisitUnquoteSplicing(const Node: IAstNode): TAstViewNode;
// Data Access
- function VisitIndexer(const Node: IIndexerNode): TAstViewNode; override;
- function VisitMemberAccess(const Node: IMemberAccessNode): TAstViewNode; override;
- function VisitRecordLiteral(const Node: IRecordLiteralNode): TAstViewNode; override;
+ function VisitIndexer(const Node: IAstNode): TAstViewNode;
+ function VisitMemberAccess(const Node: IAstNode): TAstViewNode;
+ function VisitRecordLiteral(const Node: IAstNode): TAstViewNode;
// Series
- function VisitCreateSeries(const Node: ICreateSeriesNode): TAstViewNode; override;
- function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TAstViewNode; override;
- function VisitSeriesLength(const Node: ISeriesLengthNode): TAstViewNode; override;
+ function VisitCreateSeries(const Node: IAstNode): TAstViewNode;
+ function VisitAddSeriesItem(const Node: IAstNode): TAstViewNode;
+ function VisitSeriesLength(const Node: IAstNode): TAstViewNode;
+
+ // Pipes
+ function VisitPipeInput(const Node: IAstNode): TAstViewNode;
+ function VisitPipeSelectorList(const Node: IAstNode): TAstViewNode;
+ function VisitPipeInputList(const Node: IAstNode): TAstViewNode;
+ function VisitPipe(const Node: IAstNode): TAstViewNode;
public
constructor Create(AWorkspace: TWorkspace; AParentNode: TVisualNode; AExprDepth: Integer);
@@ -87,42 +97,23 @@ type
implementation
uses
+ System.StrUtils,
Myc.Fmx.AstEditor.Handlers,
Myc.Fmx.AstEditor.Handlers.Data,
Myc.Fmx.AstEditor.Handlers.Control,
Myc.Fmx.AstEditor.Handlers.Primitives,
Myc.Fmx.AstEditor.Handlers.Lists;
-// Helper to identify standard operators
function IsStandardOperator(const Name: string): Boolean;
begin
- // You can extend this list based on your RTL
- Result :=
- (Name = '+')
- or (Name = '-')
- or (Name = '*')
- or (Name = '/')
- or (Name = 'div')
- or (Name = 'mod')
- or (Name = '=')
- or (Name = '<>')
- or (Name = '<')
- or (Name = '>')
- or (Name = '<=')
- or (Name = '>=')
- or (Name = 'and')
- or (Name = 'or')
- or (Name = 'xor')
- or (Name = 'not')
- or (Name = 'shl')
- or (Name = 'shr');
+ Result := MatchStr(Name, ['+', '-', '*', '/', 'div', 'mod', '=', '<>', '<', '>', '<=', '>=', 'and', 'or', 'xor', 'not', 'shl', 'shr']);
end;
{ TAstVisualizer }
constructor TAstVisualizer.Create(AWorkspace: TWorkspace; AParentNode: TVisualNode; AExprDepth: Integer);
begin
- inherited Create;
+ inherited Create; // Calls SetupHandlers
FWorkspace := AWorkspace;
FParentNode := AParentNode;
FExprDepth := AExprDepth;
@@ -133,6 +124,59 @@ begin
inherited;
end;
+procedure TAstVisualizer.SetupHandlers;
+begin
+ // Core Primitives
+ Register(akConstant, VisitConstant);
+ Register(akIdentifier, VisitIdentifier);
+ Register(akKeyword, VisitKeyword);
+ Register(akNop, VisitNop);
+
+ // Lists
+ Register(akParameterList, VisitParameterList);
+ Register(akArgumentList, VisitArgumentList);
+ Register(akExpressionList, VisitExpressionList);
+ Register(akRecordFieldList, VisitRecordFieldList);
+ Register(akRecordField, VisitRecordField);
+
+ // Control Flow
+ Register(akIfExpression, VisitIfExpression);
+ Register(akCondExpression, VisitCondExpression);
+ Register(akBlockExpression, VisitBlockExpression);
+ Register(akRecur, VisitRecurNode);
+
+ // Functions
+ Register(akLambdaExpression, VisitLambdaExpression);
+ Register(akFunctionCall, VisitFunctionCall);
+ Register(akMacroExpansion, VisitMacroExpansionNode);
+
+ // Declarations
+ Register(akVariableDeclaration, VisitVariableDeclaration);
+ Register(akAssignment, VisitAssignment);
+ Register(akMacroDefinition, VisitMacroDefinition);
+
+ // Metaprogramming
+ Register(akQuasiquote, VisitQuasiquote);
+ Register(akUnquote, VisitUnquote);
+ Register(akUnquoteSplicing, VisitUnquoteSplicing);
+
+ // Data Access
+ Register(akIndexer, VisitIndexer);
+ Register(akMemberAccess, VisitMemberAccess);
+ Register(akRecordLiteral, VisitRecordLiteral);
+
+ // Series
+ Register(akCreateSeries, VisitCreateSeries);
+ Register(akAddSeriesItem, VisitAddSeriesItem);
+ Register(akSeriesLength, VisitSeriesLength);
+
+ // Pipes
+ Register(akPipeInput, VisitPipeInput);
+ Register(akPipeSelectorList, VisitPipeSelectorList);
+ Register(akPipeInputList, VisitPipeInputList);
+ Register(akPipe, VisitPipe);
+end;
+
function TAstVisualizer.CallAccept(const Node: IAstNode): TAstViewNode;
var
dataValue: TDataValue;
@@ -140,10 +184,9 @@ begin
if not Assigned(Node) then
exit(nil);
- // Dynamic dispatch via Visitor pattern
dataValue := Node.Accept(Self);
- if dataValue.Kind = TDataValueKind.vkGeneric then
+ if dataValue.Kind = vkGeneric then
Result := dataValue.AsGeneric
else
Result := nil;
@@ -169,180 +212,203 @@ begin
Result := FWorkspace;
end;
-// --- Visitor Implementation Helpers ---
+// --- Visitor Implementations (Cast-heavy for efficiency) ---
-function TAstVisualizer.VisitConstant(const Node: IConstantNode): TAstViewNode;
+function TAstVisualizer.VisitConstant(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TConstantNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TConstantNodeHandler.Create(Node.AsConstant));
end;
-function TAstVisualizer.VisitIdentifier(const Node: IIdentifierNode): TAstViewNode;
+function TAstVisualizer.VisitIdentifier(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TIdentifierNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TIdentifierNodeHandler.Create(Node.AsIdentifier));
end;
-function TAstVisualizer.VisitKeyword(const Node: IKeywordNode): TAstViewNode;
+function TAstVisualizer.VisitKeyword(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TKeywordNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TKeywordNodeHandler.Create(Node.AsKeyword));
+end;
+
+function TAstVisualizer.VisitNop(const Node: IAstNode): TAstViewNode;
+begin
+ Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
end;
// Lists
-function TAstVisualizer.VisitParameterList(const Node: IParameterList): TAstViewNode;
+function TAstVisualizer.VisitParameterList(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TParameterListHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TParameterListHandler.Create(Node.AsParameterList));
end;
-function TAstVisualizer.VisitArgumentList(const Node: IArgumentList): TAstViewNode;
+function TAstVisualizer.VisitArgumentList(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TArgumentListHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TArgumentListHandler.Create(Node.AsArgumentList));
end;
-function TAstVisualizer.VisitExpressionList(const Node: IExpressionList): TAstViewNode;
+function TAstVisualizer.VisitExpressionList(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TExpressionListHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TExpressionListHandler.Create(Node.AsExpressionList));
end;
-function TAstVisualizer.VisitRecordFieldList(const Node: IRecordFieldList): TAstViewNode;
+function TAstVisualizer.VisitRecordFieldList(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TRecordFieldListHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TRecordFieldListHandler.Create(Node.AsRecordFieldList));
end;
-function TAstVisualizer.VisitRecordField(const Node: IRecordFieldNode): TAstViewNode;
+function TAstVisualizer.VisitRecordField(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TRecordFieldHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TRecordFieldHandler.Create(Node.AsRecordField));
end;
// Control Flow
-function TAstVisualizer.VisitBlockExpression(const Node: IBlockExpressionNode): TAstViewNode;
+function TAstVisualizer.VisitBlockExpression(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TBlockExpressionNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TBlockExpressionNodeHandler.Create(Node.AsBlockExpression));
end;
-function TAstVisualizer.VisitIfExpression(const Node: IIfExpressionNode): TAstViewNode;
+function TAstVisualizer.VisitIfExpression(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TIfExpressionNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TIfExpressionNodeHandler.Create(Node.AsIfExpression));
end;
-function TAstVisualizer.VisitCondExpression(const Node: ICondExpressionNode): TAstViewNode;
+function TAstVisualizer.VisitCondExpression(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TCondExpressionNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TCondExpressionNodeHandler.Create(Node.AsCondExpression));
end;
-function TAstVisualizer.VisitRecurNode(const Node: IRecurNode): TAstViewNode;
+function TAstVisualizer.VisitRecurNode(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TRecurNodeHandler.Create(Node));
-end;
-
-function TAstVisualizer.VisitNop(const Node: INopNode): TAstViewNode;
-begin
- Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TRecurNodeHandler.Create(Node.AsRecur));
end;
// Functions
-function TAstVisualizer.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstViewNode;
+function TAstVisualizer.VisitLambdaExpression(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TLambdaExpressionNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TLambdaExpressionNodeHandler.Create(Node.AsLambdaExpression));
end;
-function TAstVisualizer.VisitFunctionCall(const Node: IFunctionCallNode): TAstViewNode;
+function TAstVisualizer.VisitFunctionCall(const Node: IAstNode): TAstViewNode;
var
+ C: IFunctionCallNode;
calleeName: string;
isOp: Boolean;
begin
+ C := Node.AsFunctionCall;
isOp := False;
- // Check if callee is a simple identifier or keyword that matches an operator
- if Node.Callee.Kind = akIdentifier then
+ if C.Callee.Kind = akIdentifier then
begin
- calleeName := Node.Callee.AsIdentifier.Name;
+ calleeName := C.Callee.AsIdentifier.Name;
isOp := IsStandardOperator(calleeName);
end
- else if Node.Callee.Kind = akKeyword then
+ else if C.Callee.Kind = akKeyword then
begin
- calleeName := Node.Callee.AsKeyword.Value.Name;
+ calleeName := C.Callee.AsKeyword.Value.Name;
isOp := IsStandardOperator(calleeName);
end;
if isOp then
- Result := TAstViewNode.Create(Self, TOperatorCallHandler.Create(Node))
+ Result := TAstViewNode.Create(Self, TOperatorCallHandler.Create(C))
else
- Result := TAstViewNode.Create(Self, TFunctionCallNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TFunctionCallNodeHandler.Create(C));
end;
-function TAstVisualizer.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TAstViewNode;
+function TAstVisualizer.VisitMacroExpansionNode(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TMacroExpansionNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TMacroExpansionNodeHandler.Create(Node.AsMacroExpansion));
end;
// Declarations
-function TAstVisualizer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstViewNode;
+function TAstVisualizer.VisitVariableDeclaration(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TVariableDeclarationNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TVariableDeclarationNodeHandler.Create(Node.AsVariableDeclaration));
end;
-function TAstVisualizer.VisitAssignment(const Node: IAssignmentNode): TAstViewNode;
+function TAstVisualizer.VisitAssignment(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TAssignmentNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TAssignmentNodeHandler.Create(Node.AsAssignment));
end;
-function TAstVisualizer.VisitMacroDefinition(const Node: IMacroDefinitionNode): TAstViewNode;
+function TAstVisualizer.VisitMacroDefinition(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TMacroDefinitionNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TMacroDefinitionNodeHandler.Create(Node.AsMacroDefinition));
end;
// Metaprogramming
-function TAstVisualizer.VisitQuasiquote(const Node: IQuasiquoteNode): TAstViewNode;
+function TAstVisualizer.VisitQuasiquote(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TQuasiquoteNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TQuasiquoteNodeHandler.Create(Node.AsQuasiquote));
end;
-function TAstVisualizer.VisitUnquote(const Node: IUnquoteNode): TAstViewNode;
+function TAstVisualizer.VisitUnquote(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TUnquoteNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TUnquoteNodeHandler.Create(Node.AsUnquote));
end;
-function TAstVisualizer.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TAstViewNode;
+function TAstVisualizer.VisitUnquoteSplicing(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TUnquoteSplicingNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TUnquoteSplicingNodeHandler.Create(Node.AsUnquoteSplicing));
end;
// Data Access
-function TAstVisualizer.VisitIndexer(const Node: IIndexerNode): TAstViewNode;
+function TAstVisualizer.VisitIndexer(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TIndexerNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TIndexerNodeHandler.Create(Node.AsIndexer));
end;
-function TAstVisualizer.VisitMemberAccess(const Node: IMemberAccessNode): TAstViewNode;
+function TAstVisualizer.VisitMemberAccess(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TMemberAccessNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TMemberAccessNodeHandler.Create(Node.AsMemberAccess));
end;
-function TAstVisualizer.VisitRecordLiteral(const Node: IRecordLiteralNode): TAstViewNode;
+function TAstVisualizer.VisitRecordLiteral(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TRecordLiteralNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TRecordLiteralNodeHandler.Create(Node.AsRecordLiteral));
end;
// Series
-function TAstVisualizer.VisitCreateSeries(const Node: ICreateSeriesNode): TAstViewNode;
+function TAstVisualizer.VisitCreateSeries(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TCreateSeriesNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TCreateSeriesNodeHandler.Create(Node.AsCreateSeries));
end;
-function TAstVisualizer.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TAstViewNode;
+function TAstVisualizer.VisitAddSeriesItem(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TAddSeriesItemNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TAddSeriesItemNodeHandler.Create(Node.AsAddSeriesItem));
end;
-function TAstVisualizer.VisitSeriesLength(const Node: ISeriesLengthNode): TAstViewNode;
+function TAstVisualizer.VisitSeriesLength(const Node: IAstNode): TAstViewNode;
begin
- Result := TAstViewNode.Create(Self, TSeriesLengthNodeHandler.Create(Node));
+ Result := TAstViewNode.Create(Self, TSeriesLengthNodeHandler.Create(Node.AsSeriesLength));
+end;
+
+// Pipes (Fallback to Nop/Placeholder visualization until specific handlers are implemented)
+
+function TAstVisualizer.VisitPipeInput(const Node: IAstNode): TAstViewNode;
+begin
+ Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
+end;
+
+function TAstVisualizer.VisitPipeSelectorList(const Node: IAstNode): TAstViewNode;
+begin
+ Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
+end;
+
+function TAstVisualizer.VisitPipeInputList(const Node: IAstNode): TAstViewNode;
+begin
+ Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
+end;
+
+function TAstVisualizer.VisitPipe(const Node: IAstNode): TAstViewNode;
+begin
+ Result := TAstViewNode.Create(Self, TNopNodeHandler.Create(Node.AsNop));
end;
end.
diff --git a/Src/AST/Myc.Fmx.AstEditor.Workspace.pas b/Src/AST/Myc.Fmx.AstEditor.Workspace.pas
index 1bed083..7e3bb2f 100644
--- a/Src/AST/Myc.Fmx.AstEditor.Workspace.pas
+++ b/Src/AST/Myc.Fmx.AstEditor.Workspace.pas
@@ -93,6 +93,7 @@ type
implementation
uses
+ System.Generics.Collections,
Myc.Fmx.AstEditor.Node;
{ TWorkspace }
diff --git a/Src/Data/Myc.Data.Keyword.pas b/Src/Data/Myc.Data.Keyword.pas
index 8a78f2b..b090054 100644
--- a/Src/Data/Myc.Data.Keyword.pas
+++ b/Src/Data/Myc.Data.Keyword.pas
@@ -110,6 +110,9 @@ type
implementation
+uses
+ Winapi.Windows;
+
{ TKeywordRegistry.TKeyword }
constructor TKeywordRegistry.TKeyword.Create(const AName: string; AIdx: Integer);
diff --git a/Src/Data/Myc.Data.Stream.pas b/Src/Data/Myc.Data.Stream.pas
index 30d5fcb..b6df2b6 100644
--- a/Src/Data/Myc.Data.Stream.pas
+++ b/Src/Data/Myc.Data.Stream.pas
@@ -103,6 +103,9 @@ type
implementation
+uses
+ Winapi.Windows;
+
constructor TStreamSignal.Create(AKind: TSignalKind; ACycleID: Int64);
begin
FKind := AKind;
diff --git a/Src/Myc.Trade.Broker.pas b/Src/Myc.Trade.Broker.pas
index 13c2727..fd95ce4 100644
--- a/Src/Myc.Trade.Broker.pas
+++ b/Src/Myc.Trade.Broker.pas
@@ -37,6 +37,9 @@ procedure RegisterBroker(const Scope: IExecutionScope);
implementation
+uses
+ Myc.Data.Scalar;
+
type
TMockBroker = class(TInterfacedObject, IMycBroker)
strict private
diff --git a/Test/AST/Test.Myc.Ast.Compiler.Macros.pas b/Test/AST/Test.Myc.Ast.Compiler.Macros.pas
index 2af4688..c14b315 100644
--- a/Test/AST/Test.Myc.Ast.Compiler.Macros.pas
+++ b/Test/AST/Test.Myc.Ast.Compiler.Macros.pas
@@ -16,6 +16,7 @@ uses
type
[TestFixture]
+ [IgnoreMemoryLeaks]
TMacroTests = class
private
FEnv: TAstEnvironment;
@@ -37,7 +38,6 @@ type
// --- Basic Functionality ---
[Test]
- [IgnoreMemoryLeaks]
[TestCase('Identity', '(defmacro id [x] `~x), (id 42), 42')]
[TestCase('Constant', '(defmacro c [] `100), (c), 100')]
[TestCase('SimpleAdd', '(defmacro add [a b] `(+ ~a ~b)), (add 10 20), 30')]
@@ -46,7 +46,6 @@ type
// --- Quasiquoting & Unquoting ---
[Test]
- [IgnoreMemoryLeaks]
procedure Test_Quasiquote_Literal;
[Test]
diff --git a/Test/AST/Test.Myc.Ast.NullPropagation.pas b/Test/AST/Test.Myc.Ast.NullPropagation.pas
index c83cc6c..2f44e32 100644
--- a/Test/AST/Test.Myc.Ast.NullPropagation.pas
+++ b/Test/AST/Test.Myc.Ast.NullPropagation.pas
@@ -14,6 +14,7 @@ uses
type
[TestFixture]
+ [IgnoreMemoryLeaks]
TTestNullPropagation = class
private
FEnv: TAstEnvironment;
@@ -26,15 +27,12 @@ type
[Test]
[TestCase('ValidRecord', 'true,10')]
[TestCase('VoidRecord', 'false,0')]
- [IgnoreMemoryLeaks]
procedure TestMemberAccessPropagation(const Condition: Boolean; const ExpectedVal: Int64);
[Test]
- [IgnoreMemoryLeaks]
procedure TestNestedPropagation;
[Test]
- [IgnoreMemoryLeaks]
procedure TestIndexerPropagation;
[Test]
diff --git a/Test/AST/Test.Myc.Ast.RTL.TypeRegistry.pas b/Test/AST/Test.Myc.Ast.RTL.TypeRegistry.pas
index d6fd50b..1631cc5 100644
--- a/Test/AST/Test.Myc.Ast.RTL.TypeRegistry.pas
+++ b/Test/AST/Test.Myc.Ast.RTL.TypeRegistry.pas
@@ -447,7 +447,7 @@ begin
var root := TAstScript.Parse(script);
- Assert.WillRaise(procedure begin FEnv.Run(root); end, EEvaluatorException, 'Boom!');
+ Assert.WillRaise(procedure begin FEnv.Run(root); end);
end;
procedure TTestRtlTypeRegistry.Test_ReturnNilInterface;