From 22674b962b8174a24b36e5f0caf19206b2b3e05e Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Sat, 3 Jan 2026 19:14:18 +0100 Subject: [PATCH] Generic Visitors --- ASTPlayground/ASTPlayground.delphilsp.json | 2 +- ASTPlayground/ASTPlayground.dpr | 3 +- ASTPlayground/ASTPlayground.dproj | 1 + Src/AST/Myc.Ast.Analysis.Purity.pas | 283 ++-- Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas | 50 +- Src/AST/Myc.Ast.Compiler.Binder.pas | 121 +- Src/AST/Myc.Ast.Compiler.Macros.pas | 272 ++-- Src/AST/Myc.Ast.Compiler.Specializer.pas | 62 +- Src/AST/Myc.Ast.Compiler.TCO.pas | 172 +-- Src/AST/Myc.Ast.Compiler.TypeChecker.pas | 334 +++-- Src/AST/Myc.Ast.Debugger.pas | 390 ++---- Src/AST/Myc.Ast.Dumper.pas | 536 ++++--- Src/AST/Myc.Ast.Evaluator.pas | 900 +++++------- Src/AST/Myc.Ast.JSON.pas | 1021 +++++++------- Src/AST/Myc.Ast.Nodes.pas | 4 + Src/AST/Myc.Ast.Refactoring.Remove.pas | 375 +++-- Src/AST/Myc.Ast.Script.Print.pas | 537 +++++++ Src/AST/Myc.Ast.Script.pas | 410 +----- Src/AST/Myc.Ast.Visitor.pas | 1322 +++++++++--------- Src/AST/Myc.Fmx.AstEditor.Visualizer.pas | 306 ++-- Src/AST/Myc.Fmx.AstEditor.Workspace.pas | 1 + Src/Data/Myc.Data.Keyword.pas | 3 + Src/Data/Myc.Data.Stream.pas | 3 + Src/Myc.Trade.Broker.pas | 3 + Test/AST/Test.Myc.Ast.Compiler.Macros.pas | 3 +- Test/AST/Test.Myc.Ast.NullPropagation.pas | 4 +- Test/AST/Test.Myc.Ast.RTL.TypeRegistry.pas | 2 +- 27 files changed, 3573 insertions(+), 3547 deletions(-) create mode 100644 Src/AST/Myc.Ast.Script.Print.pas 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;