diff --git a/.gitignore b/.gitignore index a63df36..95561b9 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,4 @@ __recovery/ # Boss dependency manager vendor folder https://github.com/HashLoad/boss modules/ +.vscode diff --git a/ASTPlayground/ASTPlayground.delphilsp.json b/ASTPlayground/ASTPlayground.delphilsp.json new file mode 100644 index 0000000..ba2fd30 --- /dev/null +++ b/ASTPlayground/ASTPlayground.delphilsp.json @@ -0,0 +1 @@ +{ "settings": { "project": "file:///T%3A/Myc/ASTPlayground/ASTPlayground.dpr", "dllname": "dcc64370.dll", "dccOptions": "-$O- -$W+ --inline:off -$R+ -$Q+ --no-config -Q -TX.exe -AGenerics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG;;FRAMEWORK_FMX -E.\\Win64\\Debug -I\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -LEC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Bpl\\Win64 -LNC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NU.\\Win64\\Debug -NSWinapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;System;Xml;Data;Datasnap;Web;Soap; -OT:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -R\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -U\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -V -VN -VR -NBC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NHC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\hpp\\Win64 -NO.\\Win64\\Debug -LU" , "projectFiles":[ { "name": "MainForm", "file": "file:///T%3A/Myc/ASTPlayground/MainForm.pas" }, { "name": "Myc.Ast.Evaluator", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Evaluator.pas" }, { "name": "Myc.Ast.Nodes", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Nodes.pas" }, { "name": "Myc.Ast.Scope", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Scope.pas" }, { "name": "Myc.Fmx.AstEditor", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.pas" }, { "name": "Myc.Data.Value", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Data.Value.pas" }, { "name": "Myc.Ast.Debugger", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Debugger.pas" }, { "name": "Myc.Ast.Visitor", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Visitor.pas" }, { "name": "Myc.Ast.Binding", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Binding.pas" }, { "name": "Myc.Ast.RTL", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.pas" }, { "name": "Myc.Ast.Dumper", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Dumper.pas" }, { "name": "Myc.Ast.RTL.Core", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.Core.pas" }, { "name": "Myc.Fmx.AstEditor.Node", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Node.pas" }, { "name": "Myc.Fmx.AstEditor.Workspace", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Workspace.pas" }, { "name": "Myc.Fmx.AstEditor.Text", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Text.pas" }, { "name": "Myc.Utils", "file": "file:///T%3A/Myc/Src/Myc.Utils.pas" }, { "name": "Myc.Ast.Script", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Script.pas" }, { "name": "Myc.Ast.MacroExpander", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.MacroExpander.pas" } ] , "includeDCUsInUsesCompletion": true, "enableKeyWordCompletion": false, "browsingPaths": [ "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/OCX/Servers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/VCL","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/RTL/SYS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win/winrt","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/ToolsAPI","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/IBX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Internet","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/PROPERTY%20EDITORS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/XML","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Core","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/System","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Protocols","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/fmx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/components","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/engine","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/graph","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ado","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/cloud","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dbx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dsnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/vclctrls","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/connectors","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/proxygen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DataExplorer","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/src","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/tests","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Experts","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/abstraction","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/implementation","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indyimpl","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Property%20Editors/Indy10","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap/wsdlimporter","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Visualizers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XMLReporting","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XPGen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/rest","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/firedac","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/tethering","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnitX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ems","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/net","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/FlatBox2D","file:///C%3A/Users/Public/Documents/Embarcadero/Studio/37.0/Dcp/%25PLATFORM%25","file:///%25TP%25/runtime/vcl","file:///S%3A/sc_famos_src/svn_vcl/cpxedits","file:///S%3A/sc_famos_src/svn_vcl/dzChart","file:///S%3A/sc_famos_src/svn_vcl/eaeditor","file:///S%3A/sc_famos_src/svn_vcl/FamosEvents","file:///S%3A/sc_famos_src/svn_vcl/HTML_Viewer/source","file:///S%3A/sc_famos_src/svn_vcl/Myc_ChartEdit","file:///S%3A/sc_famos_src/svn_vcl/Myc_HemiDrag","file:///S%3A/sc_famos_src/svn_vcl/Myc_SplitControl","file:///S%3A/sc_famos_src/svn_vcl/NiceChart","file:///S%3A/sc_famos_src/svn_vcl/plotter","file:///S%3A/sc_famos_src/svn_vcl/SimpleIPC","file:///S%3A/sc_famos_src/svn_vcl/SynEdit-master/Source","file:///S%3A/sc_famos_src/svn_vcl/ValEdits","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib/win32","file:///S%3A/sc_famos_src/svn_vcl/Virtual-TreeView/Source","file:///S%3A/sc_famos_src/svn_common/caHelpSys","file:///S%3A/sc_famos_src/svn_common/Misc","file:///S%3A/sc_famos_src/svn_common/Myc_VecMath","file:///S%3A/sc_famos_src/svn_common/PascalScript/source","file:///S%3A/sc_famos_src/svn_common/TreeBase","file:///S%3A/sc_famos_src/svn_common/TreeBase/Medien","file:///S%3A/3rdParty/spring4d/Source","file:///S%3A/3rdParty/spring4d/Source/Base","file:///S%3A/3rdParty/spring4d/Source/Base/Collections","file:///S%3A/3rdParty/spring4d/Source/Base/Logging","file:///S%3A/3rdParty/spring4d/Source/Base/Patches","file:///S%3A/3rdParty/spring4d/Source/Core/Container","file:///S%3A/3rdParty/spring4d/Source/Core/Interception","file:///S%3A/3rdParty/spring4d/Source/Core/Logging","file:///S%3A/3rdParty/spring4d/Source/Core/Mocking","file:///S%3A/3rdParty/spring4d/Source/Core/Services","file:///S%3A/3rdParty/spring4d/Source/Extensions/Cryptography","file:///S%3A/3rdParty/spring4d/Source/Extensions/Utils","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCommon%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressGDI%2B%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLibrary/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/XP%20Theme%20Manager/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLayout%20Control/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressPageControl/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressEditors%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDocking%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressBars/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressOfficeCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressMemData/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDataController/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressNavBar/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressSkins%20Library/Sources","file:///T%3A/Myc/Src","file:///T%3A/Myc/Src/Data","file:///T%3A/Myc/Src/AST" ] , "CommonAppData": "file:///C%3A/Users/Brummel/AppData/Roaming/Embarcadero/BDS/37.0/" , "Templates": "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/ObjRepos/" } } \ No newline at end of file diff --git a/ASTPlayground/ASTPlayground.dpr b/ASTPlayground/ASTPlayground.dpr index 17e46a3..f716d99 100644 --- a/ASTPlayground/ASTPlayground.dpr +++ b/ASTPlayground/ASTPlayground.dpr @@ -1,6 +1,7 @@ program ASTPlayground; uses + FastMM5, System.StartUpCopy, FMX.Forms, MainForm in 'MainForm.pas' {Form1}, @@ -19,8 +20,7 @@ uses Myc.Fmx.AstEditor.Workspace in 'Myc.Fmx.AstEditor.Workspace.pas', Myc.Fmx.AstEditor.Text in 'Myc.Fmx.AstEditor.Text.pas', Myc.Utils in '..\Src\Myc.Utils.pas', - Myc.Ast.Script in '..\Src\AST\Myc.Ast.Script.pas', - Myc.Ast.MacroExpander in '..\Src\AST\Myc.Ast.MacroExpander.pas'; + Myc.Ast.Script in '..\Src\AST\Myc.Ast.Script.pas'; {$R *.res} diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj index 9476190..b0aee2b 100644 --- a/ASTPlayground/ASTPlayground.dproj +++ b/ASTPlayground/ASTPlayground.dproj @@ -134,7 +134,6 @@
Form1
- fmx
@@ -152,7 +151,6 @@ - Base diff --git a/ASTPlayground/MainForm.fmx b/ASTPlayground/MainForm.fmx index 86ea512..d9bc310 100644 --- a/ASTPlayground/MainForm.fmx +++ b/ASTPlayground/MainForm.fmx @@ -20,7 +20,6 @@ object Form1: TForm1 Position.Y = 32.000000000000000000 TabOrder = 1 Text = 'Test 1' - TextSettings.Trimming = None OnClick = Test1ButtonClick end object Test2Button: TButton @@ -28,7 +27,6 @@ object Form1: TForm1 Position.Y = 62.000000000000000000 TabOrder = 2 Text = 'Test 2' - TextSettings.Trimming = None OnClick = Test2ButtonClick end object PrettyPrintButton: TButton @@ -36,7 +34,6 @@ object Form1: TForm1 Position.Y = 479.000000000000000000 TabOrder = 3 Text = 'Print' - TextSettings.Trimming = None OnClick = PrettyPrintButtonClick end object RecursionButton: TButton @@ -47,7 +44,6 @@ object Form1: TForm1 Size.PlatformDefault = False TabOrder = 4 Text = 'Recursion' - TextSettings.Trimming = None OnClick = RecursionButtonClick end object ShowScopeBox: TCheckBox @@ -61,7 +57,6 @@ object Form1: TForm1 Position.Y = 122.000000000000000000 TabOrder = 7 Text = 'Fibonacci' - TextSettings.Trimming = None OnClick = FibonacciButtonClick end object CrerateTriggerExampleButton: TButton @@ -69,7 +64,6 @@ object Form1: TForm1 Position.Y = 168.000000000000000000 TabOrder = 8 Text = 'TriggerTest' - TextSettings.Trimming = None OnClick = CreateTriggerExampleButtonClick end object DoTriggerButton: TButton @@ -77,13 +71,11 @@ object Form1: TForm1 Position.Y = 198.000000000000000000 TabOrder = 9 Text = 'Trigger!' - TextSettings.Trimming = None OnClick = DoTriggerButtonClick object DoTrigger2Button: TButton Position.Y = 30.000000000000000000 TabOrder = 5 Text = 'Trigger2' - TextSettings.Trimming = None OnClick = DoTrigger2ButtonClick end end @@ -95,7 +87,6 @@ object Form1: TForm1 Size.PlatformDefault = False TabOrder = 10 Text = 'Clear' - TextSettings.Trimming = None OnClick = ClearButtonClick end object FlowOnlyBox: TCheckBox @@ -113,7 +104,6 @@ object Form1: TForm1 Size.PlatformDefault = False TabOrder = 13 Text = 'Series' - TextSettings.Trimming = None OnClick = SeriesTestButtonClick end object OHLCButton: TButton @@ -121,7 +111,6 @@ object Form1: TForm1 Position.Y = 288.000000000000000000 TabOrder = 14 Text = 'OHLC' - TextSettings.Trimming = None OnClick = OHLCButtonClick end object DebugBox: TCheckBox @@ -135,7 +124,6 @@ object Form1: TForm1 Position.Y = 602.000000000000000000 TabOrder = 16 Text = 'From JSON' - TextSettings.Trimming = None OnClick = FromJSONButtonClick end object ToJSONButton: TButton @@ -143,7 +131,6 @@ object Form1: TForm1 Position.Y = 632.000000000000000000 TabOrder = 17 Text = 'To JSON' - TextSettings.Trimming = None OnClick = ToJSONButtonClick end object ExternalFuncButton: TButton @@ -151,7 +138,6 @@ object Form1: TForm1 Position.Y = 318.000000000000000000 TabOrder = 18 Text = 'External Func' - TextSettings.Trimming = None OnClick = ExternalFuncButtonClick end object InnerLambdaButton: TButton @@ -159,7 +145,6 @@ object Form1: TForm1 Position.Y = 348.000000000000000000 TabOrder = 19 Text = 'Inner Lambda' - TextSettings.Trimming = None OnClick = InnerLambdaButtonClick end object DumpButton: TButton @@ -167,7 +152,6 @@ object Form1: TForm1 Position.Y = 509.000000000000000000 TabOrder = 20 Text = 'Dump' - TextSettings.Trimming = None OnClick = DumpButtonClick end object FailingUpvalueButton: TButton @@ -175,7 +159,6 @@ object Form1: TForm1 Position.Y = 378.000000000000000000 TabOrder = 22 Text = 'Upvalue' - TextSettings.Trimming = None OnClick = FailingUpvalueButtonClick end object TailCallButten: TButton @@ -183,7 +166,6 @@ object Form1: TForm1 Position.Y = 406.000000000000000000 TabOrder = 23 Text = 'Tail calls' - TextSettings.Trimming = None OnClick = TailCallButtenClick end object SaveUserLibButton: TButton @@ -191,7 +173,6 @@ object Form1: TForm1 Position.Y = 680.000000000000000000 TabOrder = 24 Text = 'Save Lib' - TextSettings.Trimming = None OnClick = SaveUserLibButtonClick end object LoadUserLibButton: TButton @@ -199,7 +180,6 @@ object Form1: TForm1 Position.Y = 710.000000000000000000 TabOrder = 25 Text = 'LoadLib' - TextSettings.Trimming = None OnClick = LoadUserLibButtonClick end end diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index b94b893..95fb36e 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -33,7 +33,6 @@ uses Myc.Ast.Binding, Myc.Ast.RTL, Myc.Ast.Script, - Myc.Ast.MacroExpander, FMX.Layouts, FMX.Objects, Myc.Ast.Debugger, @@ -114,7 +113,7 @@ type FTriggerScope: IExecutionScope; FScriptUpdate: Boolean; procedure WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); - function CreateEvaluator(Scope: IExecutionScope): IEvaluatorVisitor; + function CreateEvaluator(const Scope: IExecutionScope): IEvaluatorVisitor; // Helper function to encapsulate the Bind -> Evaluate pattern function ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue; procedure UpdateScript; @@ -212,31 +211,26 @@ end; function TForm1.ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue; var binder: IAstBinder; - scriptScope: IExecutionScope; + boundAst: IAstNode; + descriptor: IScopeDescriptor; + evalScope: IExecutionScope; visitor: IEvaluatorVisitor; begin - var macroExpander := - TMacroExpander.Create( - function(const ANode: IAstNode): TDataValue - begin - var binder := TAstBinder.Create(AParentScope) as IAstBinder; + // The new, streamlined pipeline: The binder handles macro expansion internally. - var descriptor: IScopeDescriptor; - var boundAst := binder.Execute(ANode, descriptor); + // Step 1: Create a binder, passing the evaluator factory for compile-time evaluations. + binder := TAstBinder.Create(AParentScope, CreateEvaluator); - var evalScope := descriptor.CreateScope(AParentScope); - var evaluator := CreateEvaluator(evalScope); + boundAst := binder.Execute(ANode, descriptor); - Result := evaluator.Execute(boundAst); - end) - as IMacroExpander; + // Store the final bound AST for visualization and debugging. + FCurrAst := ANode; + FCurrDesc := descriptor; - binder := TAstBinder.Create(AParentScope); - FCurrAst := binder.Execute(macroExpander.Execute(ANode), FCurrDesc); - - scriptScope := FCurrDesc.CreateScope(AParentScope); - visitor := CreateEvaluator(scriptScope); - Result := visitor.Execute(FCurrAst); + // Step 2: Create the final scope and evaluator for runtime execution. + evalScope := descriptor.CreateScope(AParentScope); + visitor := CreateEvaluator(evalScope); + Result := visitor.Execute(boundAst); end; procedure TForm1.FormCreate(Sender: TObject); @@ -307,7 +301,7 @@ begin ) ); - binder := TAstBinder.Create(Scope); + binder := TAstBinder.Create(Scope, CreateEvaluator); boundSmaAst := binder.Execute(smaAst, smaDescriptor); smaScope := smaDescriptor.CreateScope(Scope); @@ -496,7 +490,7 @@ begin raise EJSONParseException.Create('Invalid JSON format.'); try - var binder := TAstBinder.Create(FGScope); + var binder := TAstBinder.Create(FGScope, CreateEvaluator); var unboundAst := converter.Deserialize(jsonObj); FCurrAst := binder.Execute(unboundAst, FCurrDesc); Memo1.Lines.Add('AST deserialized successfully from JSON.'); @@ -566,7 +560,7 @@ begin end; // Re-bind the current AST against the global scope to ensure up-to-date binding info for the dump. - binder := TAstBinder.Create(FGScope); + binder := TAstBinder.Create(FGScope, CreateEvaluator); boundNode := binder.Execute(FCurrAst, FCurrDesc); TAstDumper.Dump(boundNode, Memo1.Lines); end; @@ -608,7 +602,7 @@ begin ] ); - binder := TAstBinder.Create(FGScope); + binder := TAstBinder.Create(FGScope, CreateEvaluator); var desc: IScopeDescriptor; boundFibAst := binder.Execute(fibAst, desc); fibScope := desc.CreateScope(FGScope); @@ -902,7 +896,7 @@ begin ); // 1. Bind and execute the setup script. - binder := TAstBinder.Create(FGScope); + binder := TAstBinder.Create(FGScope, CreateEvaluator); boundSetupAst := binder.Execute(setupAst, setupDescriptor); scope := setupDescriptor.CreateScope(FGScope); @@ -915,7 +909,7 @@ begin callAst := TAst.FunctionCall(TAst.Identifier('maCrossStrategy'), [currentSeriesIdent]); // 3. Re-bind the call AST within the now-populated scope to resolve the new variable. - binder := TAstBinder.Create(scope); + binder := TAstBinder.Create(scope, CreateEvaluator); boundCallAst := binder.Execute(callAst, callDescriptor); // 4. Get the address of 'current_series' from the new descriptor. @@ -1041,7 +1035,7 @@ begin ] ); - binder := TAstBinder.Create(FGScope); + binder := TAstBinder.Create(FGScope, CreateEvaluator); FCurrAst := binder.Execute(blk, FCurrDesc); FTriggerScope := FCurrDesc.CreateScope(FGScope); @@ -1053,7 +1047,7 @@ begin UpdateScript; end; -function TForm1.CreateEvaluator(Scope: IExecutionScope): IEvaluatorVisitor; +function TForm1.CreateEvaluator(const Scope: IExecutionScope): IEvaluatorVisitor; begin if DebugBox.IsChecked then Result := TDebugEvaluatorVisitor.Create(Scope, Memo1.Lines, ShowScopeBox.IsChecked) diff --git a/Src/AST/Myc.Ast.Binding.pas b/Src/AST/Myc.Ast.Binding.pas index 0b2965d..22dfd8f 100644 --- a/Src/AST/Myc.Ast.Binding.pas +++ b/Src/AST/Myc.Ast.Binding.pas @@ -14,16 +14,29 @@ uses Myc.Ast; type - // The binder is a transformer that enriches the AST with semantic information - // like resolved addresses, scopes, and tail-call annotations. IAstBinder = interface(IAstVisitor) function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; end; + TAstBinder = class; // Forward declaration + + // This visitor handles the expansion of a single macro body (` `...`). + // It correctly distinguishes between syntactic unquoting and value unquoting. + TExpansionVisitor = class(TAstTransformer) + private + FBinder: TAstBinder; + FMacroScope: IExecutionScope; + protected + function VisitUnquote(const Node: IUnquoteNode): TDataValue; override; + function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override; + function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; + public + constructor Create(const ABinder: TAstBinder; const AMacroScope: IExecutionScope); + end; + TAstBinder = class(TAstTransformer, IAstBinder) private type - // Helper class to track upvalues for a lambda expression. TUpvalueMapping = class public Map: TDictionary; @@ -32,24 +45,23 @@ type destructor Destroy; override; end; private + FInitialScope: IExecutionScope; FCurrentDescriptor: IScopeDescriptor; FUpvalueStack: TStack; FNestedLambdaCount: Integer; FIsTailStack: TStack; FNextIsTail: Boolean; - // Contains all variable declarations that are captured by a closure. - // This is populated by a pre-pass before the transformation begins. FBoxedDeclarations: THashSet; + FEvaluatorFactory: TEvaluatorFactory; + FMacros: TDictionary; procedure EnterScope; procedure ExitScope; function IsValidIdentifier(const Name: string): Boolean; + function EvaluateAtCompileTime(const ANode: IAstNode): TDataValue; protected - // Stack management for tail-call state is centralized here. function Accept(const Node: IAstNode): TDataValue; override; - - // The binder overrides specific visit methods to enrich the AST. function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; @@ -62,10 +74,10 @@ type function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; - public - constructor Create(const AInitialScope: IExecutionScope); - destructor Destroy; override; + public + constructor Create(const AInitialScope: IExecutionScope; const AEvaluatorFactory: TEvaluatorFactory); + destructor Destroy; override; function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; end; @@ -77,7 +89,6 @@ type property Address: TResolvedAddress read FAddress; end; - // A bound variable declaration node that includes whether the variable is captured (boxed). TBoundVariableDeclarationNode = class(TVariableDeclarationNode) private FIsBoxed: Boolean; @@ -100,7 +111,6 @@ type const AUpvalues: TArray; AHasNestedLambdas: Boolean ); - property ScopeDescriptor: IScopeDescriptor read FScopeDescriptor; property Upvalues: TArray read FUpvalues; property HasNestedLambdas: Boolean read FHasNestedLambdas; @@ -126,13 +136,107 @@ uses System.Character; type - // A custom equality comparer for TResolvedAddress to ensure correct behavior in TDictionary. TResolvedAddressComparer = class(TEqualityComparer) public function Equals(const Left, Right: TResolvedAddress): Boolean; override; function GetHashCode(const Value: TResolvedAddress): Integer; override; end; +{ TExpansionVisitor } + +constructor TExpansionVisitor.Create(const ABinder: TAstBinder; const AMacroScope: IExecutionScope); +begin + inherited Create; + FBinder := ABinder; + FMacroScope := AMacroScope; +end; + +function TExpansionVisitor.VisitUnquote(const Node: IUnquoteNode): TDataValue; +var + value: TDataValue; + expr: IAstNode; + addr: TResolvedAddress; +begin + expr := Node.Expression; + + // Check if the expression is a simple identifier that refers to a macro parameter. + if (expr is TIdentifierNode) then + begin + addr := FMacroScope.CreateDescriptor.FindSymbol((expr as TIdentifierNode).Name); + if (addr.Kind = akLocalOrParent) and (addr.ScopeDepth = 0) then + begin + // It's a macro parameter. Get its value, which is the AST passed as an argument. + var argValue := FMacroScope.Values[addr]; + if argValue.Kind = vkInterface then + begin + // This is syntactic unquoting. Return the AST directly. + Result := argValue; + exit; + end; + end; + end; + + // If it's not a parameter or the parameter doesn't hold an AST, it's value unquoting. + // Evaluate the expression at compile time using the binder's context. + value := FBinder.EvaluateAtCompileTime(expr); + + // Convert the resulting value back into an AST node to splice it into the tree. + if value.Kind in [vkScalar, vkText, vkVoid] then + Result := TDataValue.FromIntf(TAst.Constant(value)) + else + // For now, other complex types are not supported for value unquoting. + raise Exception.CreateFmt('Cannot unquote complex value of type %s at compile time.', [value.Kind.ToString]); +end; + +function TExpansionVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; +begin + // Similar to VisitUnquote, but we expect the result to be a list of nodes. + // We evaluate the inner expression. The result is a TDataValue. + // We don't do anything with it here; we just return it. + // The VisitFunctionCall override will check for this and perform the "splicing". + var value := VisitUnquote(TAst.Unquote(Node.Expression)).AsIntf; + Result := TDataValue.FromIntf(TAst.UnquoteSplicing(value)); +end; + +function TExpansionVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; +var + newArgs: TList; + transformedArg: IAstNode; + splicingNode: IUnquoteSplicingNode; +begin + // This override handles splicing arguments (~@). + newArgs := TList.Create; + try + for var arg in Node.Arguments do + begin + var transformedArgValue := Self.Accept(arg); // This might return an UnquoteSplicing node. + if transformedArgValue.IsVoid then + continue; + + transformedArg := transformedArgValue.AsIntf; + + if (transformedArg is TUnquoteSplicingNode) then + begin + splicingNode := transformedArg as TUnquoteSplicingNode; + // The inner expression should have been evaluated to an AST block + if (splicingNode.Expression is TBlockExpressionNode) then + newArgs.AddRange((splicingNode.Expression as TBlockExpressionNode).Expressions) + else + raise Exception.Create('Expression inside unquote-splicing (`~@`) must evaluate to a list of nodes (a block).'); + end + else + begin + newArgs.Add(transformedArg); + end; + end; + + var transformedCallee := Self.Accept(Node.Callee).AsIntf; + Result := TDataValue.FromIntf(TAst.FunctionCall(transformedCallee, newArgs.ToArray)); + finally + newArgs.Free; + end; +end; + { TBoundIdentifierNode } constructor TBoundIdentifierNode.Create(const AUnboundNode: IIdentifierNode; const AAddress: TResolvedAddress); begin @@ -204,15 +308,23 @@ begin inherited Destroy; end; -constructor TAstBinder.Create(const AInitialScope: IExecutionScope); +{ TAstBinder } + +constructor TAstBinder.Create(const AInitialScope: IExecutionScope; const AEvaluatorFactory: TEvaluatorFactory); begin inherited Create; + Assert(Assigned(AInitialScope)); + Assert(Assigned(AEvaluatorFactory)); + + FInitialScope := AInitialScope; + FEvaluatorFactory := AEvaluatorFactory; FCurrentDescriptor := AInitialScope.CreateDescriptor; FUpvalueStack := TObjectStack.Create(True); FNestedLambdaCount := 0; FIsTailStack := TStack.Create; FNextIsTail := True; FBoxedDeclarations := nil; + FMacros := TDictionary.Create; end; destructor TAstBinder.Destroy; @@ -220,6 +332,7 @@ begin FIsTailStack.Free; FUpvalueStack.Free; FBoxedDeclarations.Free; + FMacros.Free; inherited; end; @@ -241,16 +354,46 @@ begin FCurrentDescriptor := TScope.CreateDescriptor(FCurrentDescriptor); end; +function TAstBinder.EvaluateAtCompileTime(const ANode: IAstNode): TDataValue; +var + subBinder: IAstBinder; + subDescriptor: IScopeDescriptor; + boundSubAst: IAstNode; + evalScope: IExecutionScope; + evaluator: IEvaluatorVisitor; + tempInitScope: IExecutionScope; +begin + // Create a temporary scope that represents the binder's current lexical context. + tempInitScope := TScope.CreateScope(FInitialScope.Parent, FCurrentDescriptor, nil); + + // 1. Bind the sub-tree in a new binder, using the current scope descriptor. + subBinder := TAstBinder.Create(tempInitScope, FEvaluatorFactory); + boundSubAst := subBinder.Execute(ANode, subDescriptor); + + // 2. Create the execution scope for this specific evaluation. + evalScope := subDescriptor.CreateScope(tempInitScope); + + // 3. Create the correct evaluator (Debug/Production) using the injected factory. + evaluator := FEvaluatorFactory(evalScope); + + // 4. Execute and return the resulting value. + Result := evaluator.Execute(boundSubAst); +end; + function TAstBinder.Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode; begin - // First pass: Analyze the entire AST to find all variable declarations - // that are captured by closures (upvalues). FBoxedDeclarations := TUpvalueAnalyzer.Analyze(RootNode, FCurrentDescriptor.Parent); try - // Second pass: Transform the tree, using the information from the analysis pass. EnterScope; try - Result := Accept(RootNode).AsIntf; + var transformedNode := Accept(RootNode).AsIntf; + + // If the result of the transformation is a single void constant, return an empty block instead. + if (transformedNode is TConstantNode) and (TConstantNode(transformedNode).Value.IsVoid) then + Result := TAst.Block([]) + else + Result := transformedNode; + Descriptor := FCurrentDescriptor; finally ExitScope; @@ -260,6 +403,101 @@ begin end; end; +function TAstBinder.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; +begin + // Register the macro for the current binder instance. + FMacros.AddOrSetValue(Node.Name.Name, Node); + + // Return an empty block node. This is a valid "no-op" node + // that is simply ignored by the evaluator. + Result := TDataValue.FromIntf(TAst.Block([])); +end; + +function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; +var + macroDef: IMacroDefinitionNode; + calleeIdentifier: TIdentifierNode; + i: Integer; + expansionScope: IExecutionScope; + expander: TExpansionVisitor; + expandedBody: IAstNode; + isTailCall: Boolean; + callee: IAstNode; + args: TArray; + boundCall: IFunctionCallNode; +begin + // First, check if this is a macro call. + if (Node.Callee is TIdentifierNode) then + begin + calleeIdentifier := Node.Callee as TIdentifierNode; + if FMacros.TryGetValue(calleeIdentifier.Name, macroDef) then + begin + // It's a macro. Expand it now. + expansionScope := TAst.CreateScope(nil); + + // Check for variadic macro parameter + var params := macroDef.Parameters; + var lastParamName := ''; + if Length(params) > 0 then + lastParamName := params[High(params)].Name; + + if (Length(params) > 1) and (lastParamName.StartsWith('&')) then + begin + var requiredArgs := Length(params) - 1; + if Length(Node.Arguments) < requiredArgs then + raise Exception.CreateFmt( + 'Macro %s expects at least %d arguments, but got %d', + [calleeIdentifier.Name, requiredArgs, Length(Node.Arguments)]); + + // Bind fixed arguments + for i := 0 to requiredArgs - 1 do + expansionScope.Define(params[i].Name, TDataValue.FromIntf(Node.Arguments[i])); + + // Bind rest arguments as a list (AST block) + var restArgs: TArray; + SetLength(restArgs, Length(Node.Arguments) - requiredArgs); + for i := 0 to High(restArgs) do + restArgs[i] := Node.Arguments[requiredArgs + i]; + expansionScope.Define(lastParamName.Substring(1), TDataValue.FromIntf(TAst.Block(restArgs))); + end + else + begin + if Length(Node.Arguments) <> Length(params) then + raise Exception.CreateFmt( + 'Macro %s expects %d arguments, but got %d', + [calleeIdentifier.Name, Length(params), Length(Node.Arguments)]); + + for i := 0 to High(params) do + expansionScope.Define(params[i].Name, TDataValue.FromIntf(Node.Arguments[i])); + end; + + // A macro body MUST be a quasiquote. + if not (macroDef.Body is TQuasiquoteNode) then + raise Exception.CreateFmt('Macro body for "%s" must be a quasiquoted expression.', [calleeIdentifier.Name]); + + // Use the dedicated expansion visitor to process the CONTENT of the macro body. + var quasiquoteBody := macroDef.Body as TQuasiquoteNode; + expander := TExpansionVisitor.Create(Self, expansionScope); + expandedBody := expander.Execute(quasiquoteBody.Expression); + + // IMPORTANT: Recursively call Accept on the newly generated AST fragment + // to bind it within the current scope. + Result := Self.Accept(expandedBody); + exit; + end; + end; + + // It's a regular function call, proceed with normal binding. + isTailCall := FIsTailStack.Peek; + + FNextIsTail := False; + callee := Accept(Node.Callee).AsIntf; + args := TransformNodes(Node.Arguments); + + boundCall := TBoundFunctionCallNode.Create(Node, callee, args, isTailCall); + Result := TDataValue.FromIntf(boundCall); +end; + procedure TAstBinder.ExitScope; begin FCurrentDescriptor := FCurrentDescriptor.Parent; @@ -284,6 +522,176 @@ begin Result := True; end; +function TAstBinder.VisitAssignment(const Node: IAssignmentNode): TDataValue; +begin + FNextIsTail := False; + Result := inherited VisitAssignment(Node); +end; + +function TAstBinder.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; +begin + FNextIsTail := False; + Result := inherited VisitBinaryExpression(Node); +end; + +function TAstBinder.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; +var + exprs: TArray; + i: Integer; + isContextTail: Boolean; + transformedValue: TDataValue; + exprList: TList; +begin + isContextTail := FIsTailStack.Peek; + exprList := TList.Create; + try + for i := 0 to High(Node.Expressions) do + begin + FNextIsTail := isContextTail and (i = High(Node.Expressions)); + transformedValue := Accept(Node.Expressions[i]); + // If a sub-expression (like a macro definition) returns void, skip it. + if not transformedValue.IsVoid then + exprList.Add(transformedValue.AsIntf); + end; + exprs := exprList.ToArray; + finally + exprList.Free; + end; + + // Avoid creating a new node if nothing changed. + if (Length(exprs) = Length(Node.Expressions)) then + begin + var same := True; + for i := 0 to High(exprs) do + if exprs[i] <> Node.Expressions[i] then + begin + same := False; + break; + end; + if same then + begin + Result := TDataValue.FromIntf(Node); + exit; + end; + end; + + Result := TDataValue.FromIntf(TAst.Block(exprs)); +end; + +function TAstBinder.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; +var + isContextTail: Boolean; + condition, thenBranch, elseBranch: IAstNode; +begin + isContextTail := FIsTailStack.Peek; + + FNextIsTail := False; + condition := Accept(Node.Condition).AsIntf; + + FNextIsTail := isContextTail; + thenBranch := Accept(Node.ThenBranch).AsIntf; + elseBranch := Accept(Node.ElseBranch).AsIntf; + + if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then + Result := TDataValue.FromIntf(TAst.IfExpr(condition, thenBranch, elseBranch)) + else + Result := TDataValue.FromIntf(Node); +end; + +function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; +var + i: integer; + boundParams: TArray; + boundBody: IAstNode; + lambdaScope: IScopeDescriptor; + upvalues: TArray; + hasNestedLambdas: Boolean; + lastNestedLambdaCount: Integer; + boundLambda: ILambdaExpressionNode; +begin + FUpvalueStack.Push(TUpvalueMapping.Create); + try + EnterScope; + try + FCurrentDescriptor.Define(''); + + SetLength(boundParams, Length(Node.Parameters)); + for i := 0 to High(Node.Parameters) do + begin + var paramNode := Node.Parameters[i]; + var slotIndex := FCurrentDescriptor.Define(paramNode.Name); + var address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex); + boundParams[i] := TBoundIdentifierNode.Create(paramNode, address); + end; + + lastNestedLambdaCount := FNestedLambdaCount; + + FNextIsTail := True; + boundBody := Accept(Node.Body).AsIntf; + + hasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount; + lambdaScope := FCurrentDescriptor; + finally + ExitScope; + end; + + var upvalueMapping := FUpvalueStack.Peek; + var sortedPairs := upvalueMapping.Map.ToArray; + TArray.Sort>( + sortedPairs, + TComparer>.Construct( + function(const Left, Right: TPair): Integer begin Result := Left.Value - Right.Value; end + ) + ); + + SetLength(upvalues, Length(sortedPairs)); + for i := 0 to High(sortedPairs) do + upvalues[i] := sortedPairs[i].Key; + + finally + FUpvalueStack.Pop; + end; + + inc(FNestedLambdaCount); + boundLambda := TBoundLambdaExpressionNode.Create(Node, boundBody, boundParams, lambdaScope, upvalues, hasNestedLambdas); + Result := TDataValue.FromIntf(boundLambda); +end; + +function TAstBinder.VisitRecurNode(const Node: IRecurNode): TDataValue; +begin + if not FIsTailStack.Peek then + raise Exception.Create('''recur'' can only be used in a tail position.'); + + FNextIsTail := False; + Result := inherited VisitRecurNode(Node); +end; + +function TAstBinder.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; +var + isContextTail: Boolean; + condition, thenBranch, elseBranch: IAstNode; +begin + isContextTail := FIsTailStack.Peek; + + FNextIsTail := False; + condition := Accept(Node.Condition).AsIntf; + + FNextIsTail := isContextTail; + thenBranch := Accept(Node.ThenBranch).AsIntf; + elseBranch := Accept(Node.ElseBranch).AsIntf; + + if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then + Result := TDataValue.FromIntf(TAst.TernaryExpr(condition, thenBranch, elseBranch)) + else + Result := TDataValue.FromIntf(Node); +end; + +function TAstBinder.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; +begin + FNextIsTail := False; + Result := inherited VisitUnaryExpression(Node); +end; + function TAstBinder.VisitIdentifier(const Node: IIdentifierNode): TDataValue; var adr: TResolvedAddress; @@ -346,170 +754,4 @@ begin Result := TDataValue.FromIntf(boundDecl); end; -function TAstBinder.VisitAssignment(const Node: IAssignmentNode): TDataValue; -begin - FNextIsTail := False; - Result := inherited VisitAssignment(Node); -end; - -function TAstBinder.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; -begin - FNextIsTail := False; - Result := inherited VisitBinaryExpression(Node); -end; - -function TAstBinder.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; -begin - FNextIsTail := False; - Result := inherited VisitUnaryExpression(Node); -end; - -function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; -var - i: integer; - boundParams: TArray; - boundBody: IAstNode; - lambdaScope: IScopeDescriptor; - upvalues: TArray; - hasNestedLambdas: Boolean; - lastNestedLambdaCount: Integer; - boundLambda: ILambdaExpressionNode; -begin - FUpvalueStack.Push(TUpvalueMapping.Create); - try - EnterScope; - try - FCurrentDescriptor.Define(''); - - SetLength(boundParams, Length(Node.Parameters)); - for i := 0 to High(Node.Parameters) do - begin - var paramNode := Node.Parameters[i]; - var slotIndex := FCurrentDescriptor.Define(paramNode.Name); - var address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex); - boundParams[i] := TBoundIdentifierNode.Create(paramNode, address); - end; - - lastNestedLambdaCount := FNestedLambdaCount; - - FNextIsTail := True; - boundBody := Accept(Node.Body).AsIntf; - - hasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount; - lambdaScope := FCurrentDescriptor; - finally - ExitScope; - end; - - var upvalueMapping := FUpvalueStack.Peek; - var sortedPairs := upvalueMapping.Map.ToArray; - TArray.Sort>( - sortedPairs, - TComparer>.Construct( - function(const Left, Right: TPair): Integer begin Result := Left.Value - Right.Value; end - ) - ); - - SetLength(upvalues, Length(sortedPairs)); - for i := 0 to High(sortedPairs) do - upvalues[i] := sortedPairs[i].Key; - - finally - FUpvalueStack.Pop; - end; - - inc(FNestedLambdaCount); - boundLambda := TBoundLambdaExpressionNode.Create(Node, boundBody, boundParams, lambdaScope, upvalues, hasNestedLambdas); - Result := TDataValue.FromIntf(boundLambda); -end; - -function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; -var - isTailCall: Boolean; - callee: IAstNode; - args: TArray; - boundCall: IFunctionCallNode; -begin - isTailCall := FIsTailStack.Peek; - - FNextIsTail := False; - callee := Accept(Node.Callee).AsIntf; - args := TransformNodes(Node.Arguments); - - boundCall := TBoundFunctionCallNode.Create(Node, callee, args, isTailCall); - Result := TDataValue.FromIntf(boundCall); -end; - -function TAstBinder.VisitRecurNode(const Node: IRecurNode): TDataValue; -begin - if not FIsTailStack.Peek then - raise Exception.Create('''recur'' can only be used in a tail position.'); - - FNextIsTail := False; - Result := inherited VisitRecurNode(Node); -end; - -function TAstBinder.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; -var - exprs: TArray; - i: Integer; - isContextTail: Boolean; -begin - isContextTail := FIsTailStack.Peek; - - SetLength(exprs, Length(Node.Expressions)); - for i := 0 to High(exprs) do - begin - FNextIsTail := isContextTail and (i = High(exprs)); - exprs[i] := Accept(Node.Expressions[i]).AsIntf; - end; - Result := TDataValue.FromIntf(TAst.Block(exprs)); -end; - -function TAstBinder.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; -var - isContextTail: Boolean; - condition, thenBranch, elseBranch: IAstNode; -begin - isContextTail := FIsTailStack.Peek; - - FNextIsTail := False; - condition := Accept(Node.Condition).AsIntf; - - FNextIsTail := isContextTail; - thenBranch := Accept(Node.ThenBranch).AsIntf; - elseBranch := Accept(Node.ElseBranch).AsIntf; - - if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then - Result := TDataValue.FromIntf(TAst.IfExpr(condition, thenBranch, elseBranch)) - else - Result := TDataValue.FromIntf(Node); -end; - -function TAstBinder.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; -begin - // The binder runs after the macro expander. It should never see a macro definition. - raise Exception.Create('IMacroDefinitionNode found in AST after macro expansion phase.'); -end; - -function TAstBinder.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; -var - isContextTail: Boolean; - condition, thenBranch, elseBranch: IAstNode; -begin - isContextTail := FIsTailStack.Peek; - - FNextIsTail := False; - condition := Accept(Node.Condition).AsIntf; - - FNextIsTail := isContextTail; - thenBranch := Accept(Node.ThenBranch).AsIntf; - elseBranch := Accept(Node.ElseBranch).AsIntf; - - if (condition <> Node.Condition) or (thenBranch <> Node.ThenBranch) or (elseBranch <> Node.ElseBranch) then - Result := TDataValue.FromIntf(TAst.TernaryExpr(condition, thenBranch, elseBranch)) - else - Result := TDataValue.FromIntf(Node); -end; - end. diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index 8c235b3..fda7da0 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -14,13 +14,6 @@ uses Myc.Ast.Scope; type - IEvaluatorVisitor = interface(IAstVisitor) - function Execute(const RootNode: IAstNode): TDataValue; - end; - - // A factory for creating visitors, primarily used by the debugger subsystem. - TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor; - // The standard AST evaluator for production use. TEvaluatorVisitor = class(TAstVisitor, IEvaluatorVisitor) private diff --git a/Src/AST/Myc.Ast.MacroExpander.pas b/Src/AST/Myc.Ast.MacroExpander.pas deleted file mode 100644 index 8a34aeb..0000000 --- a/Src/AST/Myc.Ast.MacroExpander.pas +++ /dev/null @@ -1,269 +0,0 @@ -unit Myc.Ast.MacroExpander; - -interface - -uses - System.SysUtils, - System.Classes, - System.Generics.Collections, - Myc.Data.Value, - Myc.Ast.Nodes, - Myc.Ast.Scope, - Myc.Ast.Visitor; - -type - // Defines the strategy for compiling and evaluating an AST node at compile time. - TCompileTimeExecutor = reference to function(const ANode: IAstNode): TDataValue; - - IMacroExpander = interface(IAstVisitor) - function Execute(const RootNode: IAstNode): IAstNode; - end; - - // The macro expander is now a pure AST transformation engine. - TMacroExpander = class(TAstTransformer, IMacroExpander) - private - FMacros: TDictionary; - FExecutor: TCompileTimeExecutor; - function ExpandNode(const ANode: IAstNode): IAstNode; - protected - function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; - function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; - public - constructor Create(const AExecutor: TCompileTimeExecutor); - destructor Destroy; override; - function Execute(const RootNode: IAstNode): IAstNode; - end; - -implementation - -uses - Myc.Ast; - -type - TExpansionHelper = class(TAstTransformer) - private - FExpansionScope: IExecutionScope; - FExpander: TMacroExpander; - FExecutor: TCompileTimeExecutor; - protected - function VisitUnquote(const Node: IUnquoteNode): TDataValue; override; - function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; override; - function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; - public - constructor Create(AExpansionScope: IExecutionScope; AExpander: TMacroExpander; AExecutor: TCompileTimeExecutor); - class function Expand( - Node: IAstNode; - ExpansionScope: IExecutionScope; - Expander: TMacroExpander; - Executor: TCompileTimeExecutor - ): IAstNode; static; - end; - - TSubstitutor = class(TAstTransformer) - private - FParamScope: IExecutionScope; - protected - function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; - public - constructor Create(AParamScope: IExecutionScope); - end; - -{ TMacroExpander } - -constructor TMacroExpander.Create(const AExecutor: TCompileTimeExecutor); -begin - inherited Create; - Assert(Assigned(AExecutor), 'A TCompileTimeExecutor must be provided.'); - FMacros := TDictionary.Create; - FExecutor := AExecutor; -end; - -destructor TMacroExpander.Destroy; -begin - FMacros.Free; - inherited; -end; - -function TMacroExpander.ExpandNode(const ANode: IAstNode): IAstNode; -var - lastNode: IAstNode; -begin - Result := ANode; - if not Assigned(Result) then - exit; - repeat - lastNode := Result; - Result := Self.Accept(lastNode).AsIntf; - until Result = lastNode; -end; - -function TMacroExpander.Execute(const RootNode: IAstNode): IAstNode; -begin - FMacros.Clear; - Result := ExpandNode(RootNode); -end; - -function TMacroExpander.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; -var - macroDef: IMacroDefinitionNode; - expandedNode: IAstNode; - i: Integer; -begin - if not (Node.Callee is TIdentifierNode) then - begin - Result := inherited VisitFunctionCall(Node); - exit; - end; - - var calleeIdentifier := Node.Callee as TIdentifierNode; - - if FMacros.TryGetValue(calleeIdentifier.Name, macroDef) then - begin - var expansionScope := TAst.CreateScope(nil); // Temporary scope for parameters, no parent needed. - - if Length(Node.Arguments) <> Length(macroDef.Parameters) then - raise Exception.CreateFmt( - 'Macro %s expects %d arguments, but got %d', - [macroDef.Name.Name, Length(macroDef.Parameters), Length(Node.Arguments)]); - - for i := 0 to High(macroDef.Parameters) do - expansionScope.Define(macroDef.Parameters[i].Name, TDataValue.FromIntf(Node.Arguments[i])); - - if not (macroDef.Body is TQuasiquoteNode) then - raise Exception.CreateFmt('Macro body for "%s" must be a quasiquoted expression.', [macroDef.Name.Name]); - - expandedNode := TExpansionHelper.Expand(IQuasiquoteNode(macroDef.Body).Expression, expansionScope, Self, FExecutor); - - Result := Self.Accept(expandedNode); - end - else - begin - Result := inherited VisitFunctionCall(Node); - end; -end; - -function TMacroExpander.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; -begin - FMacros.AddOrSetValue(Node.Name.Name, Node); - Result := TDataValue.FromIntf(TBlockExpressionNode.Create([])); -end; - -{ TExpansionHelper } - -constructor TExpansionHelper.Create(AExpansionScope: IExecutionScope; AExpander: TMacroExpander; AExecutor: TCompileTimeExecutor); -begin - inherited Create; - FExpansionScope := AExpansionScope; - FExpander := AExpander; - FExecutor := AExecutor; -end; - -class function TExpansionHelper.Expand( - Node: IAstNode; - ExpansionScope: IExecutionScope; - Expander: TMacroExpander; - Executor: TCompileTimeExecutor -): IAstNode; -var - helper: TExpansionHelper; -begin - helper := TExpansionHelper.Create(ExpansionScope, Expander, Executor); - try - Result := helper.Accept(Node).AsIntf; - finally - helper.Free; - end; -end; - -function TExpansionHelper.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; -var - newArgs: TList; - transformedArgValue: TDataValue; - transformedArgNode: IAstNode; - transformedCallee: IAstNode; -begin - newArgs := TList.Create; - try - for var arg in Node.Arguments do - begin - transformedArgValue := Self.Accept(arg); - if transformedArgValue.IsVoid then - continue; - transformedArgNode := transformedArgValue.AsIntf; - if (transformedArgNode is TBlockExpressionNode) then - newArgs.AddRange((transformedArgNode as TBlockExpressionNode).Expressions) - else - newArgs.Add(transformedArgNode); - end; - transformedCallee := Self.Accept(Node.Callee).AsIntf; - Result := TDataValue.FromIntf(TAst.FunctionCall(transformedCallee, newArgs.ToArray)); - finally - newArgs.Free; - end; -end; - -function TExpansionHelper.VisitUnquote(const Node: IUnquoteNode): TDataValue; -var - substitutor: TSubstitutor; - substitutedAst, expandedAst: IAstNode; - value: TDataValue; -begin - // Step 1: Substitute macro parameters with their AST node values. - substitutor := TSubstitutor.Create(FExpansionScope); - try - substitutedAst := substitutor.Execute(Node.Expression); - finally - substitutor.Free; - end; - - // Step 2: Recursively expand any macros within the substituted expression. - expandedAst := FExpander.ExpandNode(substitutedAst); - - // Step 3: Call the injected executor strategy to bind and evaluate the AST. - value := FExecutor(expandedAst); - - // Step 4: Wrap the resulting value for insertion into the template. - if value.Kind in [vkScalar, vkText] then - Result := TDataValue.FromIntf(TAst.Constant(value)) - else - Result := value; -end; - -function TExpansionHelper.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue; -var - unquotedValue: TDataValue; - unquotedNode: IAstNode; -begin - unquotedValue := Self.VisitUnquote(TAst.Unquote(Node.Expression)); - if unquotedValue.Kind = vkInterface then - unquotedNode := unquotedValue.AsIntf - else - unquotedNode := nil; - if not (unquotedNode is TBlockExpressionNode) then - raise Exception.Create('Expression inside unquote-splicing (`~@`) must evaluate to a list of nodes (a block).'); - Result := unquotedValue; -end; - -{ TSubstitutor } -constructor TSubstitutor.Create(AParamScope: IExecutionScope); -begin - inherited Create; - FParamScope := AParamScope; -end; - -function TSubstitutor.VisitIdentifier(const Node: IIdentifierNode): TDataValue; -var - addr: TResolvedAddress; - nodeValue: TDataValue; -begin - addr := FParamScope.CreateDescriptor.FindSymbol(Node.Name); - if (addr.Kind = akLocalOrParent) and (addr.ScopeDepth = 0) then - begin - nodeValue := FParamScope.Values[addr]; - Result := nodeValue; - exit; - end; - Result := inherited VisitIdentifier(Node); -end; - -end. diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index e96f439..38faa60 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -263,6 +263,10 @@ type property Series: IIdentifierNode read GetSeries; end; + IEvaluatorVisitor = interface(IAstVisitor) + function Execute(const RootNode: IAstNode): TDataValue; + end; + implementation uses diff --git a/Src/AST/Myc.Ast.Scope.pas b/Src/AST/Myc.Ast.Scope.pas index 4529eba..e09c2c6 100644 --- a/Src/AST/Myc.Ast.Scope.pas +++ b/Src/AST/Myc.Ast.Scope.pas @@ -61,6 +61,9 @@ type property Symbols: TDictionary read GetSymbols; end; + // A factory for creating visitors, primarily used by the debugger subsystem. + TEvaluatorFactory = reference to function(const Scope: IExecutionScope): IEvaluatorVisitor; + TScope = record class function CreateScope( const Parent: IExecutionScope; diff --git a/Src/AST/Myc.Ast.Script.pas b/Src/AST/Myc.Ast.Script.pas index 430595d..db7ab02 100644 --- a/Src/AST/Myc.Ast.Script.pas +++ b/Src/AST/Myc.Ast.Script.pas @@ -366,7 +366,13 @@ begin if not (Length(tailNodes) in [2, 3]) then raise Exception.Create('Syntax Error in if statement.'); - Result := TAst.IfExpr(tailNodes[0], tailNodes[1], IfThen(Length(tailNodes) > 2, tailNodes[2], nil)) + Result := + TAst.IfExpr( + tailNodes[0], + tailNodes[1], + if Length(tailNodes) > 2 then tailNodes[2] + else nil + ) end else if SameText(head.Token.Text, '?') then begin diff --git a/Src/AST/Myc.Ast.Visitor.pas b/Src/AST/Myc.Ast.Visitor.pas index dc2ba36..312dbd9 100644 --- a/Src/AST/Myc.Ast.Visitor.pas +++ b/Src/AST/Myc.Ast.Visitor.pas @@ -147,7 +147,11 @@ function TAstTransformer.VisitIfExpression(const Node: IIfExpressionNode): TData begin var condition := Accept(Node.Condition).AsIntf; var thenBranch := Accept(Node.ThenBranch).AsIntf; - var elseBranch := Accept(Node.ElseBranch).AsIntf; + var elseBranchValue := Accept(Node.ElseBranch); + var elseBranch := + if elseBranchValue.IsVoid then nil + else elseBranchValue.AsIntf; + if (condition = Node.Condition) and (thenBranch = Node.ThenBranch) and (elseBranch = Node.ElseBranch) then Result := TDataValue.FromIntf(Node) else diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index ab0aef7..a74549c 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -74,6 +74,7 @@ type public constructor Create(const AValue: TDataValue); function Accept(const Visitor: IAstVisitor): TDataValue; override; + property Value: TDataValue read GetValue; end; TIdentifierNode = class(TAstNode, IIdentifierNode) @@ -169,6 +170,7 @@ type public constructor Create(const AExpression: IAstNode); function Accept(const Visitor: IAstVisitor): TDataValue; override; + property Expression: IAstNode read GetExpression; end; TUnquoteNode = class(TAstNode, IUnquoteNode) @@ -865,7 +867,13 @@ end; class function TAst.IfExpr(const ACondition: IAstNode; const AThenBranch, AElseBranch: IAstNode): IIfExpressionNode; begin - Result := TIfExpressionNode.Create(ACondition, AThenBranch, AElseBranch); + Result := + TIfExpressionNode.Create( + ACondition, + AThenBranch, + if AElseBranch = nil then TBlockExpressionNode.Create([]) as IAstNode + else AElseBranch + ); end; class function TAst.Indexer(const ABase: IAstNode; const AIndex: IAstNode): IIndexerNode; diff --git a/Test/MycTests.dpr b/Test/MycTests.dpr index d6ecb7d..9fc575c 100644 --- a/Test/MycTests.dpr +++ b/Test/MycTests.dpr @@ -7,13 +7,13 @@ program MycTests; uses FastMM5, DUnitX.MemoryLeakMonitor.FastMM5, - System.SysUtils, {$IFDEF TESTINSIGHT} TestInsight.DUnitX, {$ELSE} DUnitX.Loggers.Console, {$ENDIF } DUnitX.TestFramework, + System.SysUtils, TestNotifier in 'TestNotifier.pas', TestNotifier_Threading in 'TestNotifier_Threading.pas' {/TestNotifier_ChaosStress in 'TestNotifier_ChaosStress.pas',}, TestNotifier_ChaosStress in 'TestNotifier_ChaosStress.pas', diff --git a/Test/MycTests.dproj b/Test/MycTests.dproj index e6e465b..0194e3f 100644 --- a/Test/MycTests.dproj +++ b/Test/MycTests.dproj @@ -159,10 +159,10 @@ $(PreBuildEvent)]]> MycTests.dpr - Embarcadero C++Builder Office 2000 Servers Package - Embarcadero C++Builder Office XP Servers Package - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components