diff --git a/ASTPlayground/ASTPlayground.delphilsp.json b/ASTPlayground/ASTPlayground.delphilsp.json
index cb6f69c..b4e5cd8 100644
--- a/ASTPlayground/ASTPlayground.delphilsp.json
+++ b/ASTPlayground/ASTPlayground.delphilsp.json
@@ -1 +1 @@
-{ "settings": { "project": "file:///T%3A/Myc/ASTPlayground/ASTPlayground.dpr", "dllname": "dcc64370.dll", "dccOptions": "-$O- -$W+ --inline:off -$R+ -$Q+ --no-config -Q -TX.exe -AGenerics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG;;FRAMEWORK_FMX -E.\\Win64\\Debug -I\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -LEC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Bpl\\Win64 -LNC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NU.\\Win64\\Debug -NSWinapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;System;Xml;Data;Datasnap;Web;Soap; -OT:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -R\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -U\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -V -VN -VR -NBC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NHC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\hpp\\Win64 -NO.\\Win64\\Debug -LU" , "projectFiles":[ { "name": "MainForm", "file": "file:///T%3A/Myc/ASTPlayground/MainForm.pas" }, { "name": "Myc.Ast.Evaluator", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Evaluator.pas" }, { "name": "Myc.Ast.Nodes", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Nodes.pas" }, { "name": "Myc.Ast.Scope", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Scope.pas" }, { "name": "Myc.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.RTL", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.pas" }, { "name": "Myc.Ast.Dumper", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Dumper.pas" }, { "name": "Myc.Ast.RTL.Core", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.RTL.Core.pas" }, { "name": "Myc.Fmx.AstEditor.Node", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Node.pas" }, { "name": "Myc.Fmx.AstEditor.Workspace", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Workspace.pas" }, { "name": "Myc.Fmx.AstEditor.Text", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Text.pas" }, { "name": "Myc.Utils", "file": "file:///T%3A/Myc/Src/Myc.Utils.pas" }, { "name": "Myc.Ast.Script", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Script.pas" }, { "name": "Myc.Ast.Types", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Types.pas" }, { "name": "Myc.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.Environment", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Environment.pas" }, { "name": "Myc.Ast.Compiler.Specializer", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Compiler.Specializer.pas" } ] , "includeDCUsInUsesCompletion": true, "enableKeyWordCompletion": false, "browsingPaths": [ "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/OCX/Servers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/VCL","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/RTL/SYS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/win/winrt","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/ToolsAPI","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/IBX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Internet","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/PROPERTY%20EDITORS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/SOURCE/XML","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Core","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/System","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Indy10/Protocols","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/fmx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/components","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/engine","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/databinding/graph","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ado","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/cloud","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dbx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/dsnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/vclctrls","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/connectors","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/datasnap/proxygen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DataExplorer","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/Common/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/src","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/tests","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Experts","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/abstraction","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indy/implementation","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/indyimpl","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Property%20Editors/Indy10","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/soap/wsdlimporter","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/Visualizers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XMLReporting","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnit/Contrib/XPGen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/rest","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/firedac","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/tethering","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/DUnitX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/data/ems","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/rtl/net","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/source/FlatBox2D","file:///C%3A/Users/Public/Documents/Embarcadero/Studio/37.0/Dcp/%25PLATFORM%25","file:///%25TP%25/runtime/vcl","file:///S%3A/sc_famos_src/svn_vcl/cpxedits","file:///S%3A/sc_famos_src/svn_vcl/dzChart","file:///S%3A/sc_famos_src/svn_vcl/eaeditor","file:///S%3A/sc_famos_src/svn_vcl/FamosEvents","file:///S%3A/sc_famos_src/svn_vcl/HTML_Viewer/source","file:///S%3A/sc_famos_src/svn_vcl/Myc_ChartEdit","file:///S%3A/sc_famos_src/svn_vcl/Myc_HemiDrag","file:///S%3A/sc_famos_src/svn_vcl/Myc_SplitControl","file:///S%3A/sc_famos_src/svn_vcl/NiceChart","file:///S%3A/sc_famos_src/svn_vcl/plotter","file:///S%3A/sc_famos_src/svn_vcl/SimpleIPC","file:///S%3A/sc_famos_src/svn_vcl/SynEdit-master/Source","file:///S%3A/sc_famos_src/svn_vcl/ValEdits","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib","file:///S%3A/sc_famos_src/svn_vcl/ZipMaster/zlib/win32","file:///S%3A/sc_famos_src/svn_vcl/Virtual-TreeView/Source","file:///S%3A/sc_famos_src/svn_common/caHelpSys","file:///S%3A/sc_famos_src/svn_common/Misc","file:///S%3A/sc_famos_src/svn_common/Myc_VecMath","file:///S%3A/sc_famos_src/svn_common/PascalScript/source","file:///S%3A/sc_famos_src/svn_common/TreeBase","file:///S%3A/sc_famos_src/svn_common/TreeBase/Medien","file:///S%3A/3rdParty/spring4d/Source","file:///S%3A/3rdParty/spring4d/Source/Base","file:///S%3A/3rdParty/spring4d/Source/Base/Collections","file:///S%3A/3rdParty/spring4d/Source/Base/Logging","file:///S%3A/3rdParty/spring4d/Source/Base/Patches","file:///S%3A/3rdParty/spring4d/Source/Core/Container","file:///S%3A/3rdParty/spring4d/Source/Core/Interception","file:///S%3A/3rdParty/spring4d/Source/Core/Logging","file:///S%3A/3rdParty/spring4d/Source/Core/Mocking","file:///S%3A/3rdParty/spring4d/Source/Core/Services","file:///S%3A/3rdParty/spring4d/Source/Extensions/Cryptography","file:///S%3A/3rdParty/spring4d/Source/Extensions/Utils","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressCommon%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressGDI%2B%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLibrary/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/XP%20Theme%20Manager/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressLayout%20Control/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressPageControl/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressEditors%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDocking%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressBars/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressOfficeCore%20Library/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressMemData/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressDataController/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressNavBar/Sources","file:///C%3A/Program%20Files%20%28x86%29/DevExpress%20VCL/ExpressSkins%20Library/Sources","file:///T%3A/Myc/Src","file:///T%3A/Myc/Src/Data","file:///T%3A/Myc/Src/AST" ] , "CommonAppData": "file:///C%3A/Users/Brummel/AppData/Roaming/Embarcadero/BDS/37.0/" , "Templates": "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/37.0/ObjRepos/" } }
\ No newline at end of file
+{ "settings": { "project": "file:///T%3A/Myc/ASTPlayground/ASTPlayground.dpr", "dllname": "dcc64370.dll", "dccOptions": "-$O- -$W+ --inline:off -$R+ -$Q+ --no-config -Q -TX.exe -AGenerics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG;;FRAMEWORK_FMX -E.\\Win64\\Debug -I\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -LEC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Bpl\\Win64 -LNC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NU.\\Win64\\Debug -NSWinapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;System;Xml;Data;Datasnap;Web;Soap; -OT:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -R\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -U\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\\DE\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\\DE\";T:\\Myc\\Src\\Data;T:\\Myc\\Src\\AST;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\lib\\Win64\\release\";C:\\Users\\Brummel\\Documents\\Embarcadero\\Studio\\37.0\\Imports\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\include\";S:\\sc_famos_src\\svn_vcl\\Virtual-TreeView\\Source;S:\\sc_famos_src\\svn_vcl\\cpxedits;S:\\sc_famos_src\\svn_vcl\\dzChart;S:\\sc_famos_src\\svn_vcl\\eaeditor;S:\\sc_famos_src\\svn_vcl\\FamosEvents;S:\\sc_famos_src\\svn_vcl\\HTML_Viewer\\source;S:\\sc_famos_src\\svn_vcl\\Myc_ChartEdit;S:\\sc_famos_src\\svn_vcl\\Myc_HemiDrag;S:\\sc_famos_src\\svn_vcl\\Myc_SplitControl;S:\\sc_famos_src\\svn_vcl\\plotter;S:\\sc_famos_src\\svn_vcl\\SimpleIPC;S:\\sc_famos_src\\svn_vcl\\SynEdit-master\\Source;S:\\sc_famos_src\\svn_vcl\\ValEdits;S:\\sc_famos_src\\svn_vcl\\ZipMaster;S:\\sc_famos_src\\svn_common\\Myc_VecMath;\"C:\\Program Files (x86)\\DevExpress VCL\\Library\\RS28\\Win64\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressCommon Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressGDI+ Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLibrary\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\XP Theme Manager\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressLayout Control\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressPageControl\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressEditors Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDocking Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressBars\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressOfficeCore Library\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressMemData\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressDataController\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressNavBar\\Sources\";\"C:\\Program Files (x86)\\DevExpress VCL\\ExpressSkins Library\\Sources\";S:\\3rdParty\\FastMM5;C:\\Users\\Brummel\\AppData\\Local\\Programs\\TestInsight\\Source;T:\\Myc\\Src;\"c:\\program files (x86)\\embarcadero\\studio\\37.0\\source\\DUnitX\" -V -VN -VR -NBC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\Dcp\\Win64 -NHC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\37.0\\hpp\\Win64 -NO.\\Win64\\Debug -LU" , "projectFiles":[ { "name": "MainForm", "file": "file:///T%3A/Myc/ASTPlayground/MainForm.pas" }, { "name": "Myc.Ast.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.Fmx.AstEditor.Node", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Node.pas" }, { "name": "Myc.Fmx.AstEditor.Workspace", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Workspace.pas" }, { "name": "Myc.Fmx.AstEditor.Text", "file": "file:///T%3A/Myc/ASTPlayground/Myc.Fmx.AstEditor.Text.pas" }, { "name": "Myc.Utils", "file": "file:///T%3A/Myc/Src/Myc.Utils.pas" }, { "name": "Myc.Ast.Script", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Script.pas" }, { "name": "Myc.Ast.Types", "file": "file:///T%3A/Myc/Src/AST/Myc.Ast.Types.pas" }, { "name": "Myc.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" } ] , "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 fa80f4e..bc030d8 100644
--- a/ASTPlayground/ASTPlayground.dpr
+++ b/ASTPlayground/ASTPlayground.dpr
@@ -5,11 +5,9 @@ uses
System.StartUpCopy,
FMX.Forms,
MainForm in 'MainForm.pas' {Form1},
- Myc.Ast.Evaluator in '..\Src\AST\Myc.Ast.Evaluator.pas',
Myc.Ast.Nodes in '..\Src\AST\Myc.Ast.Nodes.pas',
Myc.Ast.Scope in '..\Src\AST\Myc.Ast.Scope.pas',
Myc.Data.Value in 'Myc.Data.Value.pas',
- Myc.Ast.Debugger in '..\Src\AST\Myc.Ast.Debugger.pas',
Myc.Ast.Visitor in '..\Src\AST\Myc.Ast.Visitor.pas',
Myc.Ast.RTL in '..\Src\AST\Myc.Ast.RTL.pas',
Myc.Ast.Dumper in '..\Src\AST\Myc.Ast.Dumper.pas',
@@ -26,8 +24,9 @@ uses
Myc.Ast.Compiler.Lowering in '..\Src\AST\Myc.Ast.Compiler.Lowering.pas',
Myc.Ast.Compiler.TypeChecker in '..\Src\AST\Myc.Ast.Compiler.TypeChecker.pas',
Myc.Ast.Compiler.Macros in '..\Src\AST\Myc.Ast.Compiler.Macros.pas',
+ Myc.Ast.Compiler.Specializer in '..\Src\AST\Myc.Ast.Compiler.Specializer.pas',
Myc.Ast.Environment in '..\Src\AST\Myc.Ast.Environment.pas',
- Myc.Ast.Compiler.Specializer in '..\Src\AST\Myc.Ast.Compiler.Specializer.pas';
+ Myc.Ast.Debugger in '..\Src\AST\Myc.Ast.Debugger.pas';
{$R *.res}
diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj
index fbf97dc..541fa8b 100644
--- a/ASTPlayground/ASTPlayground.dproj
+++ b/ASTPlayground/ASTPlayground.dproj
@@ -135,11 +135,9 @@
-
-
@@ -156,8 +154,9 @@
-
+
+
Base
@@ -213,12 +212,6 @@
true
-
-
- ASTPlayground.rsm
- true
-
-
ASTPlayground.exe
diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas
index 990b709..fdad466 100644
--- a/ASTPlayground/MainForm.pas
+++ b/ASTPlayground/MainForm.pas
@@ -115,6 +115,7 @@ type
FWorkspace: TAuraWorkspace;
FScriptUpdate: Boolean;
FTriggerTest: TAstEnvironment;
+
procedure WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
// Helper function to encapsulate the Compile -> Evaluate pattern
@@ -152,7 +153,15 @@ procedure TForm1.InnerLambdaButtonClick(Sender: TObject);
var
mainBlock: IAstNode;
resultValue: TDataValue;
+ resInt: Int64;
begin
+ Memo1.Lines.Clear;
+ Memo1.Lines.Add('--- Nested Lambda & Upvalue Mutation Test ---');
+ Memo1.Lines.Add('Scenario: outer() -> defines x=10');
+ Memo1.Lines.Add(' inner() -> calls innermost()');
+ Memo1.Lines.Add(' innermost() -> captures x, adds 5');
+ Memo1.Lines.Add('Expected Result: 15');
+
mainBlock :=
TAst.Block(
[
@@ -200,6 +209,19 @@ begin
resultValue := ExecuteAst(mainBlock);
+ if (resultValue.Kind = vkScalar) and (resultValue.AsScalar.Kind = TScalar.TKind.Ordinal) then
+ begin
+ resInt := resultValue.AsScalar.Value.AsInt64;
+ Memo1.Lines.Add(Format('Actual Result: %d', [resInt]));
+
+ if resInt = 15 then
+ Memo1.Lines.Add('SUCCESS: Deeply nested closure modified captured variable correctly.')
+ else
+ Memo1.Lines.Add(Format('FAILURE: Upvalue logic broken. Expected 15, got %d.', [resInt]));
+ end
+ else
+ Memo1.Lines.Add('FAILURE: Result is not an integer scalar.');
+
Assert(TScalar.FromInt64(15) = resultValue.AsScalar, 'The final result should be 15, but is ' + resultValue.AsScalar.ToString);
UpdateScript;
end;
@@ -220,11 +242,13 @@ begin
if CompilerStageBox.ItemIndex > 1 then
begin
- var desc: IScopeDescriptor;
- Result := FEnvironment.Environment.Bind(Result, desc);
+ var layout: IScopeLayout;
+ // Bind now returns the bound AST and provides the layout
+ Result := FEnvironment.Environment.Bind(Result, layout);
if CompilerStageBox.ItemIndex > 2 then
- Result := FEnvironment.Environment.Specialize(Result, desc);
+ // Specialize no longer needs descriptor
+ Result := FEnvironment.Environment.Specialize(Result);
end;
end;
end;
@@ -247,6 +271,7 @@ begin
FEnvironment.SetStandardMode;
try
+ // Wrap in Lambda to compile
var funcDef := TAst.LambdaExpr([], ANode);
FCurrExec := FEnvironment.Compile(funcDef).Func;
Result := FCurrExec([]);
@@ -381,11 +406,11 @@ begin
'''
(defmacro stopwatch [body]
`(do
- (def start-time (timestamp))
- (def result ~body)
- (print "(calculated in " (- (timestamp) start-time) " ms)" )
- result
- ))
+ (def start-time (timestamp))
+ (def result ~body)
+ (print "(calculated in " (- (timestamp) start-time) " ms)" )
+ result
+ ))
''')
.AsMacroDefinition
);
@@ -402,6 +427,7 @@ var
funcAst: IAstNode;
funcValue: TDataValue;
converter: IJsonAstConverter;
+ addr: TResolvedAddress; // Variable für das Ergebnis von Resolve
begin
// Load definitions from JSON and populate the global scope.
if not TFile.Exists(UserLibName) then
@@ -419,7 +445,7 @@ begin
try
Memo1.Lines.Add(Format('--- Loading User Library from %s ---', [ExtractFileName(UserLibName)]));
- var scopeDescr := FEnvironment.RootScope.Descriptor;
+
// Populate the list view with loaded functions and macros.
RTLListView.Items.BeginUpdate;
try
@@ -431,8 +457,10 @@ begin
// First, deserialize the AST node from JSON.
funcAst := converter.Deserialize(pair.JsonValue as TJSONObject);
- var sym := scopeDescr.FindSymbol(pair.JsonString.Value);
- if sym.Address.Kind = akUnresolved then
+ // KORREKTUR: Statt Descriptor.FindSymbol nutzen wir Scope.Resolve
+ addr := FEnvironment.RootScope.Resolve(pair.JsonString.Value);
+
+ if addr.Kind = akUnresolved then
begin
// Distinguish between loading a macro and loading a function.
if funcAst.Kind = akMacroDefinition then
@@ -451,6 +479,7 @@ begin
end
else
Memo1.Lines.Add(Format('Symbol "%s" already defined', [pair.JsonString.Value]));
+
// Add the new function/macro to the RTL list view.
RTLListView.Items.Add.Text := pair.JsonString.Value;
end;
@@ -615,7 +644,7 @@ begin
try
FCurrUnboundAst := converter.Deserialize(jsonObj);
- // Run the full pipeline
+ // Run the full pipeline via environment
FCurrExec := FEnvironment.Compile(FCurrUnboundAst).Func;
Memo1.Lines.Add('AST deserialized and bound successfully from JSON.');
@@ -674,13 +703,13 @@ begin
Memo1.Lines.Clear;
Memo1.Lines.Add('--- AST Dump ---');
- if not Assigned(FCurrExec) then // Check FCurrExec
+ if not Assigned(FCurrExec) then
begin
Memo1.Lines.Add('No *compiled* AST has been generated yet. Click a test button first.');
exit;
end;
- // Dump the compiled AST from the executable
+ // Dump the compiled AST from the visualizer helper
TAstDumper.Dump(CompileAstStage, Memo1.Lines);
end;
@@ -688,7 +717,6 @@ procedure TForm1.FibonacciButtonClick(Sender: TObject);
var
result: TDataValue;
sw: TStopwatch;
- fibDataValue: TDataValue;
begin
Memo1.Lines.Clear;
@@ -1319,15 +1347,6 @@ begin
exit;
FWorkspace.Build(CompileAstStage, TPointF.Create(X, Y));
- //
- // if FCurrExec <> nil then
- // begin
- // FWorkspace.Build(FCurrExec.CompiledAst, TPointF.Create(X, Y));
- // end
- // else if FCurrUnboundAst <> nil then
- // begin
- // FWorkspace.Build(FCurrUnboundAst, TPointF.Create(X, Y));
- // end;
end;
end.
diff --git a/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas b/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas
index 6df7686..167478b 100644
--- a/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas
+++ b/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas
@@ -16,10 +16,29 @@ type
// This visitor analyzes the AST to find all variables that need to be "lifted" or "boxed"
// because they are captured by a nested lambda.
TUpvalueAnalyzer = class(TAstTransformer)
+ private
+ type
+ // Internal helper to track scopes during analysis without using IScopeDescriptor
+ TAnalysisScope = class
+ private
+ FParent: TAnalysisScope;
+ // Maps Name -> DeclarationNode.
+ // If Value is nil, it means the name is defined (e.g. parameter) but not a candidate for boxing.
+ FDeclarations: TDictionary;
+ public
+ constructor Create(AParent: TAnalysisScope);
+ destructor Destroy; override;
+
+ procedure Define(const Name: string; const Node: IVariableDeclarationNode);
+
+ // Returns the scope where the symbol is defined and the declaration node (if any)
+ function Resolve(const Name: string; out ScopeDepth: Integer; out Node: IVariableDeclarationNode): Boolean;
+ end;
+
private
FBoxedDeclarations: THashSet;
- FCurrentDescriptor: IScopeDescriptor;
- FDeclarationMap: TDictionary>;
+ FCurrentScope: TAnalysisScope;
+
procedure MarkDeclarationForBoxing(const AName: string);
protected
// Overridden Visit methods to perform analysis during traversal.
@@ -27,13 +46,12 @@ type
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
public
- constructor Create(const AParent: IScopeDescriptor);
+ constructor Create;
destructor Destroy; override;
- // Added Execute method
function Execute(const ARootNode: IAstNode): IAstNode;
- class function Analyze(const ARootNode: IAstNode; const AParent: IScopeDescriptor): THashSet; static;
+ class function Analyze(const ARootNode: IAstNode): THashSet; static;
end;
implementation
@@ -42,39 +60,88 @@ uses
System.Generics.Defaults,
Myc.Ast.Types;
+{ TUpvalueAnalyzer.TAnalysisScope }
+
+constructor TUpvalueAnalyzer.TAnalysisScope.Create(AParent: TAnalysisScope);
+begin
+ inherited Create;
+ FParent := AParent;
+ FDeclarations := TDictionary.Create;
+end;
+
+destructor TUpvalueAnalyzer.TAnalysisScope.Destroy;
+begin
+ FDeclarations.Free;
+ inherited;
+end;
+
+procedure TUpvalueAnalyzer.TAnalysisScope.Define(const Name: string; const Node: IVariableDeclarationNode);
+begin
+ FDeclarations.AddOrSetValue(Name, Node);
+end;
+
+function TUpvalueAnalyzer.TAnalysisScope.Resolve(const Name: string; out ScopeDepth: Integer; out Node: IVariableDeclarationNode): Boolean;
+var
+ current: TAnalysisScope;
+begin
+ ScopeDepth := 0;
+ current := Self;
+ while Assigned(current) do
+ begin
+ if current.FDeclarations.TryGetValue(Name, Node) then
+ begin
+ Result := True;
+ exit;
+ end;
+ Inc(ScopeDepth);
+ current := current.FParent;
+ end;
+
+ Node := nil;
+ Result := False;
+end;
+
{ TUpvalueAnalyzer }
-constructor TUpvalueAnalyzer.Create(const AParent: IScopeDescriptor);
+constructor TUpvalueAnalyzer.Create;
begin
inherited Create;
FBoxedDeclarations := THashSet.Create;
- FDeclarationMap :=
- TObjectDictionary>
- .Create([doOwnsValues], TEqualityComparer.Default);
- FCurrentDescriptor := TScope.CreateDescriptor(AParent);
+ // Create a root scope to handle top-level definitions cleanly
+ FCurrentScope := TAnalysisScope.Create(nil);
end;
destructor TUpvalueAnalyzer.Destroy;
begin
- FDeclarationMap.Free;
+ // Unwind scope stack if exception occurred or Execute wasn't fully clean
+ while Assigned(FCurrentScope.FParent) do
+ begin
+ var temp := FCurrentScope;
+ FCurrentScope := FCurrentScope.FParent;
+ temp.Free;
+ end;
+ FCurrentScope.Free;
+
FBoxedDeclarations.Free;
inherited Destroy;
end;
function TUpvalueAnalyzer.Execute(const ARootNode: IAstNode): IAstNode;
begin
- // Accept will call the Visit... methods and traverse the tree
Result := Accept(ARootNode);
end;
-class function TUpvalueAnalyzer.Analyze(const ARootNode: IAstNode; const AParent: IScopeDescriptor): THashSet;
+class function TUpvalueAnalyzer.Analyze(const ARootNode: IAstNode): THashSet;
var
- analyzer: TUpvalueAnalyzer; // Changed to concrete type
+ analyzer: TUpvalueAnalyzer;
begin
+ // Note: AParent (IScopeDescriptor) removed from arguments as this analyzer
+ // builds its own structural view and only cares about boxing internal variables.
+
if not Assigned(ARootNode) then
exit(THashSet.Create);
- analyzer := TUpvalueAnalyzer.Create(AParent);
+ analyzer := TUpvalueAnalyzer.Create;
try
analyzer.Execute(ARootNode);
Result := analyzer.FBoxedDeclarations;
@@ -86,124 +153,68 @@ end;
procedure TUpvalueAnalyzer.MarkDeclarationForBoxing(const AName: string);
var
- symbol: TResolvedSymbol;
- declarationScope: IScopeDescriptor;
- i: Integer;
+ depth: Integer;
+ declNode: IVariableDeclarationNode;
begin
- symbol := FCurrentDescriptor.FindSymbol(AName);
- if symbol.Address.Kind <> akLocalOrParent then
- exit;
-
- // Walk up the scope chain to find the scope where the variable was declared.
- declarationScope := FCurrentDescriptor;
- for i := 1 to symbol.Address.ScopeDepth do
+ // Check if the symbol exists in our analysis scopes
+ if FCurrentScope.Resolve(AName, depth, declNode) then
begin
- if not Assigned(declarationScope.Parent) then
- exit; // Should not happen in a correctly bound tree
- declarationScope := declarationScope.Parent;
- end;
-
- // Find the declaration node in our map and add it to the set.
- var dict: TDictionary;
- if FDeclarationMap.TryGetValue(declarationScope, dict) then
- begin
- var declNode: IVariableDeclarationNode;
- if dict.TryGetValue(AName, declNode) then
+ // If it is found in a parent scope (Depth > 0) AND we have a trackable declaration node for it
+ if (depth > 0) and Assigned(declNode) then
+ begin
FBoxedDeclarations.Add(declNode);
+ end;
end;
end;
function TUpvalueAnalyzer.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
-var
- symbol: TResolvedSymbol;
begin
- if Assigned(FCurrentDescriptor) then
- begin
- symbol := FCurrentDescriptor.FindSymbol(Node.Name);
+ // Check if this identifier refers to a variable from an outer scope
+ MarkDeclarationForBoxing(Node.Name);
- if (symbol.Address.Kind = akLocalOrParent) and (symbol.Address.ScopeDepth > 0) then
- begin
- // This is an upvalue. Mark its original declaration for boxing.
- MarkDeclarationForBoxing(Node.Name);
- end;
- end;
-
- // This is a leaf node, do not call inherited.
+ // Return original node (Analysis pass only)
Result := Node;
end;
function TUpvalueAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
- newParams: TArray;
- newBody: IAstNode;
i: Integer;
begin
- // A lambda creates a new lexical scope, inheriting from the current one.
- FCurrentDescriptor := TScope.CreateDescriptor(FCurrentDescriptor);
+ // 1. Enter new analysis scope
+ FCurrentScope := TAnalysisScope.Create(FCurrentScope);
try
- // Manually visit parameters to define them
- var oldParams := Node.Parameters;
- SetLength(newParams, Length(oldParams));
- for i := 0 to High(oldParams) do
+ // 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 High(Node.Parameters) do
begin
- // Accept(param) will call VisitIdentifier, which does its job.
- // We *must* use the result of Accept.
- newParams[i] := Accept(oldParams[i]).AsIdentifier;
- FCurrentDescriptor.Define(newParams[i].Name, TTypes.Unknown);
+ FCurrentScope.Define(Node.Parameters[i].Name, nil);
end;
- // Traverse the lambda body within the new scope context.
- newBody := Accept(Node.Body); // Manual traversal
+ // 3. Visit Body
+ Accept(Node.Body); // Recursive call
- // Rebuild node if changed
- if (newParams = Node.Parameters) and (newBody = Node.Body) then
- Result := Node
- else
- // Use factory, pass through other properties (which are nil/false at this stage)
- Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
+ // Rebuild if needed (default CoW behavior)
+ Result := Node;
finally
- // Restore the parent scope after leaving the lambda.
- FCurrentDescriptor := FCurrentDescriptor.Parent;
+ // 4. Exit scope
+ var temp := FCurrentScope;
+ FCurrentScope := FCurrentScope.FParent;
+ temp.Free;
end;
end;
function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
-var
- scopeDeclarations: TDictionary;
- newInitializer: IAstNode;
- newIdentifier: IIdentifierNode;
begin
- // Traverse the initializer first. It's evaluated in the current scope
- // before the new variable is defined.
+ // 1. Visit initializer first (it executes in the CURRENT scope)
if Assigned(Node.Initializer) then
- newInitializer := Accept(Node.Initializer)
- else
- newInitializer := nil;
+ Accept(Node.Initializer);
- // Traverse the identifier
- // This calls VisitIdentifier, but VisitIdentifier is just an analyzer, it returns the same node.
- newIdentifier := Accept(Node.Identifier).AsIdentifier;
+ // 2. Define the variable in the CURRENT scope
+ // Store the Node reference so we can add it to FBoxedDeclarations if captured.
+ FCurrentScope.Define(Node.Identifier.Name, Node);
- // After processing the initializer, define the variable in the current scope.
- // We use TTypes.Unknown as type inference hasn't run yet.
- FCurrentDescriptor.Define(newIdentifier.Name, TTypes.Unknown);
-
- // --- Rebuild Node ---
- // Use CoW, check if anything changed
- if (newInitializer = Node.Initializer) and (newIdentifier = Node.Identifier) then
- Result := Node
- else
- // Use factory, pass through other properties (which are nil/false at this stage)
- Result := TAst.VarDecl(newIdentifier, newInitializer, Node.StaticType, Node.IsBoxed);
-
- // Map this *new* declaration node to its scope and name for later lookup.
- if not FDeclarationMap.TryGetValue(FCurrentDescriptor, scopeDeclarations) then
- begin
- scopeDeclarations := TDictionary.Create;
- FDeclarationMap.Add(FCurrentDescriptor, scopeDeclarations);
- end;
- // IMPORTANT: Store the *new* node (Result)
- scopeDeclarations.Add(newIdentifier.Name, Result.AsVariableDeclaration);
+ Result := Node;
end;
end.
diff --git a/Src/AST/Myc.Ast.Compiler.Binder.pas b/Src/AST/Myc.Ast.Compiler.Binder.pas
index e8fdba0..7aed155 100644
--- a/Src/AST/Myc.Ast.Compiler.Binder.pas
+++ b/Src/AST/Myc.Ast.Compiler.Binder.pas
@@ -18,55 +18,54 @@ uses
type
IAstBinder = interface(IAstVisitor)
- // AArgTypes is now passed to the implementation via its constructor
- function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
+ function Execute(const RootNode: IAstNode; out Layout: IScopeLayout): IAstNode;
end;
- TAstBinder = class; // Forward declaration
-
TAstBinder = class(TAstTransformer, IAstBinder)
private
type
- TUpvalueMapping = TDictionary;
+ TUpvalueMap = TDictionary;
+
private
- FCurrentDescriptor: IScopeDescriptor;
- FUpvalueStack: TStack;
- FNestedLambdaCount: Integer;
- FBoxedDeclarations: THashSet;
+ FCurrentBuilder: IScopeBuilder;
+ FUpvalueStack: TObjectStack;
+
FArgTypes: TArray;
FFunctionRegistry: IFunctionDefinitionRegistry;
+ FBoxedDeclarations: THashSet;
+
+ // Fix for TExecutionScope parent chain optimization
+ FLambdaCounter: Integer;
- procedure EnterScope;
- procedure ExitScope;
function IsValidIdentifier(const Name: string): Boolean;
+ function ResolveSymbol(const Name: string; out Address: TResolvedAddress): Boolean;
+ function CaptureUpvalue(const PhysicalAddress: TResolvedAddress): Integer;
protected
- // --- Core Binding Logic (Mutators) ---
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;
-
- // --- Transformation Logic (Restored) ---
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
- // --- Standard Traversal (Use inherited) ---
- function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
function VisitConstant(const Node: IConstantNode): IAstNode; override;
+ function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode; override;
+
public
constructor Create(
- const AInitialDescriptor: IScopeDescriptor;
+ const AParentLayout: IScopeLayout;
const AFunctionRegistry: IFunctionDefinitionRegistry;
const AArgTypes: TArray
);
destructor Destroy; override;
- function Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
+
+ function Execute(const RootNode: IAstNode; out Layout: IScopeLayout): IAstNode;
class function Bind(
- const InitialDescriptor: IScopeDescriptor;
+ const ParentLayout: IScopeLayout;
const RootNode: IAstNode;
- out Descriptor: IScopeDescriptor;
+ out Layout: IScopeLayout;
const AFunctionRegistry: IFunctionDefinitionRegistry = nil;
const AArgTypes: TArray = nil
): IAstNode; static;
@@ -77,6 +76,7 @@ implementation
uses
System.Generics.Defaults,
System.Character,
+ System.Hash,
Myc.Data.Keyword;
type
@@ -86,7 +86,6 @@ type
function GetHashCode(const Value: TResolvedAddress): Integer; override;
end;
-{ TResolvedAddressComparer }
function TResolvedAddressComparer.Equals(const Left, Right: TResolvedAddress): Boolean;
begin
Result := (Left = Right);
@@ -94,29 +93,28 @@ end;
function TResolvedAddressComparer.GetHashCode(const Value: TResolvedAddress): Integer;
begin
- Result := 17;
- Result := Result * 23 + Ord(Value.Kind);
- Result := Result * 23 + Value.ScopeDepth;
- Result := Result * 23 + Value.SlotIndex;
+ Result := THashBobJenkins.GetHashValue(Value.Kind, SizeOf(TAddressKind), 0);
+ Result := THashBobJenkins.GetHashValue(Value.ScopeDepth, SizeOf(Integer), Result);
+ Result := THashBobJenkins.GetHashValue(Value.SlotIndex, SizeOf(Integer), Result);
end;
{ TAstBinder }
constructor TAstBinder.Create(
- const AInitialDescriptor: IScopeDescriptor;
+ const AParentLayout: IScopeLayout;
const AFunctionRegistry: IFunctionDefinitionRegistry;
const AArgTypes: TArray
);
begin
inherited Create;
- Assert(Assigned(AInitialDescriptor));
+ FCurrentBuilder := TScope.CreateBuilder(AParentLayout);
+ FUpvalueStack := TObjectStack.Create(True);
+ FUpvalueStack.Push(TUpvalueMap.Create(TResolvedAddressComparer.Create));
- FCurrentDescriptor := AInitialDescriptor;
FFunctionRegistry := AFunctionRegistry;
FArgTypes := AArgTypes;
- FUpvalueStack := TObjectStack.Create(True); // Use Comparer
- FNestedLambdaCount := 0;
FBoxedDeclarations := nil;
+ FLambdaCounter := 0;
end;
destructor TAstBinder.Destroy;
@@ -127,25 +125,30 @@ begin
end;
class function TAstBinder.Bind(
- const InitialDescriptor: IScopeDescriptor;
+ const ParentLayout: IScopeLayout;
const RootNode: IAstNode;
- out Descriptor: IScopeDescriptor;
+ out Layout: IScopeLayout;
const AFunctionRegistry: IFunctionDefinitionRegistry = nil;
const AArgTypes: TArray = nil
): IAstNode;
begin
- var binder := TAstBinder.Create(InitialDescriptor, AFunctionRegistry, AArgTypes) as IAstBinder;
- Result := binder.Execute(RootNode, Descriptor);
+ var binder := TAstBinder.Create(ParentLayout, AFunctionRegistry, AArgTypes);
+ try
+ Result := binder.Execute(RootNode, Layout);
+ finally
+ binder.Free;
+ end;
end;
-procedure TAstBinder.EnterScope;
+function TAstBinder.Execute(const RootNode: IAstNode; out Layout: IScopeLayout): IAstNode;
begin
- FCurrentDescriptor := TScope.CreateDescriptor(FCurrentDescriptor);
-end;
+ FBoxedDeclarations := TUpvalueAnalyzer.Analyze(RootNode);
-procedure TAstBinder.ExitScope;
-begin
- FCurrentDescriptor := FCurrentDescriptor.Parent;
+ Result := Accept(RootNode);
+ if not Assigned(Result) then
+ Result := TAst.Block([]);
+
+ Layout := FCurrentBuilder.Build;
end;
function TAstBinder.IsValidIdentifier(const Name: string): Boolean;
@@ -158,141 +161,176 @@ begin
if not (c.IsLetter or (c = '_')) then
exit(False);
for c in Name do
- begin
if not (c.IsLetterOrDigit or (c = '_') or (c = '-')) then
exit(False);
- end;
Result := True;
end;
-function TAstBinder.Execute(const RootNode: IAstNode; out Descriptor: IScopeDescriptor): IAstNode;
+function TAstBinder.ResolveSymbol(const Name: string; out Address: TResolvedAddress): Boolean;
+var
+ layout: IScopeLayout;
+ depth: Integer;
+ slot: Integer;
begin
- // Pre-pass: Find all variables that need boxing
- FBoxedDeclarations := TUpvalueAnalyzer.Analyze(RootNode, FCurrentDescriptor.Parent);
- try
- EnterScope;
- try
- Result := Accept(RootNode); // Accept returns IAstNode
- if not Assigned(Result) then
- Result := TAst.Block([]);
+ layout := FCurrentBuilder;
+ depth := 0;
- // Set the type of the root expression (e.g., the final 'do' block)
- Descriptor := FCurrentDescriptor;
- finally
- ExitScope;
+ while Assigned(layout) do
+ begin
+ slot := layout.FindSlot(Name);
+ if slot >= 0 then
+ begin
+ Address := TResolvedAddress.Create(akLocalOrParent, depth, slot);
+ Result := True;
+ exit;
end;
- finally
- FBoxedDeclarations.Free; // Free the set
- FBoxedDeclarations := nil;
+ layout := layout.Parent;
+ Inc(depth);
end;
+
+ Result := False;
end;
-function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
+function TAstBinder.CaptureUpvalue(const PhysicalAddress: TResolvedAddress): Integer;
+var
+ currentMap: TUpvalueMap;
begin
- // --- Transformation: Keyword-as-Function ---
- // This transformation must happen *before* type checking.
- if Node.Callee.Kind = akKeyword then
+ currentMap := FUpvalueStack.Peek;
+ if not currentMap.TryGetValue(PhysicalAddress, Result) then
begin
- var keywordNode := Node.Callee.AsKeyword;
- if Length(Node.Arguments) <> 1 then
- raise EArgumentException.CreateFmt(
- 'Keyword :%s expects exactly one argument (the record/map), but got %d',
- [keywordNode.Value.Name, Length(Node.Arguments)]);
-
- // Manually visit the argument (which is the base)
- var baseNode := Accept(Node.Arguments[0]);
- // Manually visit the keyword (which is the member)
- var memberNode := Accept(keywordNode).AsKeyword;
-
- // Create the new MemberAccess node
- var memberAccessNode := TAst.MemberAccess(baseNode, memberNode);
-
- // Visit the *new* node to bind it (though it has no bindable symbols)
- Result := Accept(memberAccessNode);
- exit;
+ Result := currentMap.Count;
+ currentMap.Add(PhysicalAddress, Result);
end;
-
- // --- Default: Bind as a standard function call ---
- // Use the inherited implementation to visit children (Callee, Arguments)
- Result := inherited VisitFunctionCall(Node);
-
- // Set metadata for *this* node (IsTailCall is set later by TCO)
end;
function TAstBinder.VisitConstant(const Node: IConstantNode): IAstNode;
begin
- // Binding pass does not assign types.
Result := Node;
end;
function TAstBinder.VisitKeyword(const Node: IKeywordNode): IAstNode;
begin
- // Binding pass does not assign types.
Result := Node;
end;
function TAstBinder.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode;
begin
- // Binding pass does not assign types.
Result := Node;
end;
+function TAstBinder.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
+var
+ physAddr: TResolvedAddress;
+ upvalueIndex: Integer;
+begin
+ if Node.Address.Kind = akUnresolved then
+ begin
+ if not ResolveSymbol(Node.Name, physAddr) then
+ raise Exception.CreateFmt('Undefined identifier: "%s"', [Node.Name]);
+
+ if physAddr.ScopeDepth > 0 then
+ begin
+ upvalueIndex := CaptureUpvalue(physAddr);
+ Result := TAst.Identifier(Node.Name, TResolvedAddress.Create(akUpvalue, 0, upvalueIndex), TTypes.Unknown);
+ end
+ else
+ Result := TAst.Identifier(Node.Name, physAddr, TTypes.Unknown);
+ end
+ else
+ Result := Node;
+end;
+
+function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
+var
+ slot: Integer;
+ addr: TResolvedAddress;
+ newInit: IAstNode;
+ newIdent: IIdentifierNode;
+ isBoxed: Boolean;
+begin
+ if not IsValidIdentifier(Node.Identifier.Name) then
+ raise Exception.CreateFmt('Invalid identifier name: "%s".', [Node.Identifier.Name]);
+
+ slot := FCurrentBuilder.Define(Node.Identifier.Name);
+ addr := TResolvedAddress.Create(akLocalOrParent, 0, slot);
+
+ if Assigned(Node.Initializer) then
+ newInit := Accept(Node.Initializer)
+ else
+ newInit := nil;
+
+ newIdent := TAst.Identifier(Node.Identifier.Name, addr, TTypes.Unknown);
+ isBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
+
+ Result := TAst.VarDecl(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;
+var
+ newIdent: IAstNode;
+ newValue: IAstNode;
+begin
+ newIdent := Accept(Node.Identifier);
+ newValue := Accept(Node.Value);
+
+ Result := TAst.Assign(newIdent.AsIdentifier, newValue, TTypes.Unknown);
+
+ if Assigned(FFunctionRegistry) and (newValue <> nil) and (newValue.Kind = akLambdaExpression) then
+ begin
+ if newIdent.AsIdentifier.Address.Kind = akLocalOrParent then
+ FFunctionRegistry.Register(newIdent.AsIdentifier.Address, newValue.AsLambdaExpression);
+ end;
+end;
+
function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
var
- i: integer;
- adr: TResolvedAddress;
- scopeDescriptor: IScopeDescriptor;
- upvalues: TArray;
- hasNestedLambdas: Boolean;
- lastNestedLambdaCount: Integer;
+ parentBuilder: IScopeBuilder;
+ paramType: IStaticType;
newParams: TArray;
newBody: IAstNode;
- paramType: IStaticType;
+ finalLayout: IScopeLayout;
+ i, slot: Integer;
+ addr: TResolvedAddress;
+ capturedMap: TUpvalueMap;
+ sortedPairs: TArray>;
+ upvaluesList: TArray;
+
+ startCount: Integer;
+ hasNested: Boolean;
begin
- // We do *not* call inherited, as we must manage the scope manually.
+ // Count logic: Measure how many lambdas are visited inside this one.
+ startCount := FLambdaCounter;
+ Inc(FLambdaCounter); // Count self
+
+ parentBuilder := FCurrentBuilder;
+ FCurrentBuilder := TScope.CreateBuilder(parentBuilder);
+
+ capturedMap := TUpvalueMap.Create(TResolvedAddressComparer.Create);
+ FUpvalueStack.Push(capturedMap);
- FUpvalueStack.Push(TUpvalueMapping.Create(TResolvedAddressComparer.Create));
try
- EnterScope;
- try
- // Define (slot 0)
- FCurrentDescriptor.Define('', TTypes.Unknown);
+ FCurrentBuilder.Define('');
- // Define parameters
- SetLength(newParams, Length(Node.Parameters));
- for i := 0 to High(Node.Parameters) do
- begin
- var paramNode := Node.Parameters[i]; // This is a TIdentifierNode
+ SetLength(newParams, Length(Node.Parameters));
+ for i := 0 to High(Node.Parameters) do
+ begin
+ slot := FCurrentBuilder.Define(Node.Parameters[i].Name);
+ addr := TResolvedAddress.Create(akLocalOrParent, 0, slot);
- // Check if this is the root lambda (stack count = 1)
- // and we have pre-defined types for it (from specialization)
- paramType := TTypes.Unknown;
- if (FUpvalueStack.Count = 1) and (FArgTypes <> nil) and (i < Length(FArgTypes)) then
- paramType := FArgTypes[i]; // Use the specialized type
+ paramType := TTypes.Unknown;
+ if (parentBuilder.Parent = nil) and (FArgTypes <> nil) and (i < Length(FArgTypes)) then
+ paramType := FArgTypes[i];
- // Define the symbol in the descriptor WITH the type
- var slotIndex := FCurrentDescriptor.Define(paramNode.Name, paramType);
- adr := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
-
- // Create a new TIdentifierNode, now with the correct type
- newParams[i] := TAst.Identifier(paramNode.Name, adr, paramType);
- end;
-
- // Visit the body *within the new scope*
- lastNestedLambdaCount := FNestedLambdaCount;
- newBody := Accept(Node.Body);
- hasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount;
-
- // Save the descriptor for the evaluator
- scopeDescriptor := FCurrentDescriptor;
- finally
- ExitScope;
+ newParams[i] := TAst.Identifier(Node.Parameters[i].Name, addr, paramType);
end;
- // --- Extract Upvalues ---
- var upvalueMapping := FUpvalueStack.Peek;
- var sortedPairs := upvalueMapping.ToArray;
- // Sort by index (Value)
+ newBody := Accept(Node.Body);
+ finalLayout := FCurrentBuilder.Build;
+
+ sortedPairs := capturedMap.ToArray;
TArray.Sort>(
sortedPairs,
TComparer>.Construct(
@@ -300,122 +338,38 @@ begin
)
);
- SetLength(upvalues, Length(sortedPairs));
- for i := 0 to High(upvalues) do
- upvalues[i] := sortedPairs[i].Key;
+ SetLength(upvaluesList, Length(sortedPairs));
+ for i := 0 to High(sortedPairs) do
+ begin
+ var rawAddr := sortedPairs[i].Key;
+ if rawAddr.Kind = akLocalOrParent then
+ begin
+ Assert(rawAddr.ScopeDepth > 0, 'Logic Error: Trying to capture a local variable.');
+ Dec(rawAddr.ScopeDepth);
+ end;
+ upvaluesList[i] := rawAddr;
+ end;
finally
+ FCurrentBuilder := parentBuilder;
FUpvalueStack.Pop;
end;
- inc(FNestedLambdaCount);
+ // Calculate HasNested: If counter increased by more than 1 (self), we have children.
+ hasNested := FLambdaCounter > (startCount + 1);
- // Create new immutable node using the factory
- Result := TAst.LambdaExpr(newParams, newBody, scopeDescriptor, upvalues, hasNestedLambdas);
+ Result := TAst.LambdaExpr(newParams, newBody, finalLayout, nil, upvaluesList, hasNested);
end;
-function TAstBinder.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
-var
- symbol: TResolvedSymbol;
- adr: TResolvedAddress;
+function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
begin
- // We only bind nodes that are the base parser type
- // (i.e., address is unresolved).
- if Node.Address.Kind = akUnresolved then
+ if Node.Callee.Kind = akKeyword then
begin
- symbol := FCurrentDescriptor.FindSymbol(Node.Name);
- adr := symbol.Address;
-
- if adr.Kind = akUnresolved then
- raise Exception.CreateFmt('Undefined identifier: "%s"', [Node.Name]);
-
- if adr.Kind = akLocalOrParent then
- begin
- if (adr.ScopeDepth > 0) and (FUpvalueStack.Count > 0) then
- begin
- // --- Handle Upvalue ---
- var upvalueMap := FUpvalueStack.Peek;
- // Adjust address to be relative to the captured scope
- dec(adr.ScopeDepth);
-
- var upvalueIndex: Integer;
- if not upvalueMap.TryGetValue(adr, upvalueIndex) then
- begin
- // This is a new upvalue for this lambda
- upvalueIndex := upvalueMap.Count;
- upvalueMap.Add(adr, upvalueIndex);
- end;
-
- // Create final upvalue address
- adr := TResolvedAddress.Create(akUpvalue, 0, upvalueIndex);
- end;
- // else: It's a local (ScopeDepth=0), adr is already correct.
- end;
- // else: It was already an upvalue (e.g. nested lambda), adr is correct.
-
- // --- Replace Node ---
- Result := TAst.Identifier(Node.Name, adr, symbol.StaticType);
- end
- else
- begin
- // It's already bound (e.g., a parameter), just return it.
- Result := Node;
- end;
-end;
-
-function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
-var
- slotIndex: Integer;
- address: TResolvedAddress;
- isBoxed: Boolean;
- newInitializer: IAstNode;
- newIdentifier: IIdentifierNode;
-begin
- // 1. Validate name
- if not IsValidIdentifier(Node.Identifier.Name) then
- raise Exception.CreateFmt('Invalid identifier name: "%s".', [Node.Identifier.Name]);
-
- // 2. Define the symbol in the scope *first*.
- // This allows the initializer (e.g., a lambda) to recursively refer to this symbol.
- slotIndex := FCurrentDescriptor.Define(Node.Identifier.Name, TTypes.Unknown);
- address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
-
- // 3. Visit the Initializer *after* the symbol is defined
- if Assigned(Node.Initializer) then
- newInitializer := Accept(Node.Initializer)
- else
- newInitializer := nil;
-
- // 4. Create the new (bound) identifier node
- // The TypeChecker will set the final type
- newIdentifier := TAst.Identifier(Node.Identifier.Name, address, TTypes.Unknown);
-
- // 5. Check if this declaration needs boxing (from UpvalueAnalyzer pass)
- isBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
-
- // 6. Create the new VariableDeclaration node using the factory
- Result := TAst.VarDecl(newIdentifier, newInitializer, TTypes.Unknown, isBoxed);
-
- // 7. Store in registry *only if the registry was provided*
- // (This must happen *after* visiting the initializer)
- if Assigned(FFunctionRegistry) and (newInitializer <> nil) and (newInitializer.Kind = akLambdaExpression) then
- FFunctionRegistry.Register(address, newInitializer.AsLambdaExpression);
-end;
-
-function TAstBinder.VisitAssignment(const Node: IAssignmentNode): IAstNode;
-var
- newIdent: IIdentifierNode;
- newValue: IAstNode;
-begin
- Result := inherited VisitAssignment(Node);
-
- newValue := Result.AsAssignment.Value;
- newIdent := Result.AsAssignment.Identifier;
-
- if Assigned(FFunctionRegistry) and (newValue <> nil) and (newValue.Kind = akLambdaExpression) then
- begin
- FFunctionRegistry.Register(newIdent.Address, newValue.AsLambdaExpression);
+ var memberAccess := TAst.MemberAccess(Accept(Node.Arguments[0]), Node.Callee.AsKeyword);
+ Result := memberAccess;
+ exit;
end;
+ Result := inherited VisitFunctionCall(Node);
end;
end.
diff --git a/Src/AST/Myc.Ast.Compiler.Macros.pas b/Src/AST/Myc.Ast.Compiler.Macros.pas
index 62ce301..6810b31 100644
--- a/Src/AST/Myc.Ast.Compiler.Macros.pas
+++ b/Src/AST/Myc.Ast.Compiler.Macros.pas
@@ -13,79 +13,55 @@ uses
Myc.Ast.Scope,
Myc.Ast.Types,
Myc.Ast,
- Myc.Ast.Environment; // Added for IMacroRegistry
+ Myc.Ast.Environment;
type
IAstMacroExpander = interface(IAstVisitor)
- // Signatur geaendert: Descriptor wird nicht mehr zurueckgegeben
function Execute(const RootNode: IAstNode): IAstNode;
end;
- // Callback type for compile-time evaluation of Unquote expressions
TEvaluateProc = reference to function(const Node: IAstNode): TDataValue;
- // --- TExpansionVisitor (Moved from Binding) ---
- // This visitor handles the expansion of a *single* macro body (` `...).
TExpansionVisitor = class(TAstTransformer)
private
FEvaluate: TEvaluateProc;
FMacroScope: IExecutionScope;
- FRenameMap: TDictionary; // [*] Hygiene Rename Map (Rule 1)
- FDefinitionDescriptor: IScopeDescriptor; // [*] Template Scope (Rule 1)
+ FRenameMap: TDictionary;
class var
FGensymCounter: Int64;
function TransformAndSpliceNodes(const ANodes: TArray): TArray;
-
- // [*] Gensym helper
function Gensym(const ABaseName: string): string;
- // [*] Scope management for template
- procedure EnterScope;
- procedure ExitScope;
protected
- // Override the new IAstNode-returning methods
function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override;
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
- // [*] Hygiene implementation (Rule 1)
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
-
- // [*] Hygiene implementation (Rule 1, 2, 3)
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
public
- // [*] Updated constructor
constructor Create(const AMacroScope: IExecutionScope; const AEvaluate: TEvaluateProc);
- destructor Destroy; override; // [*] Added
+ destructor Destroy; override;
- // [*] Updated static helper
class function Expand(const MacroScope: IExecutionScope; const RootNode: IAstNode; const AEvaluate: TEvaluateProc): IAstNode;
end;
- // --- TMacroExpander (New main class for Phase 1) ---
- // This transformer traverses the entire AST *before* the binder,
- // to find and expand macros.
TMacroExpander = class(TAstTransformer, IAstMacroExpander)
private
FInitialScope: IExecutionScope;
-
- FCurrentMacroRegistry: IMacroRegistry; // Changed from TMacroRegistry
-
+ FCurrentMacroRegistry: IMacroRegistry;
FEvaluatorFactory: TEvaluatorFactory;
procedure EnterMacroScope;
procedure ExitMacroScope;
protected
- // Finds macro definitions
function VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode; override;
- // Finds and expands macro calls
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
- // [*] Do not traverse into quasiquotes (they are handled by TExpansionVisitor)
function VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode; override;
function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override;
@@ -94,19 +70,17 @@ type
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
public
- // Updated Constructor
constructor Create(
const ARootRegistry: IMacroRegistry;
const AInitialScope: IExecutionScope;
const AEvaluatorFactory: TEvaluatorFactory
);
- destructor Destroy; override; // Modified
+ destructor Destroy; override;
function Execute(const RootNode: IAstNode): IAstNode;
- // Updated static helper
class function ExpandMacros(
- const ARootRegistry: IMacroRegistry; // Added
+ const ARootRegistry: IMacroRegistry;
const AInitialScope: IExecutionScope;
const RootNode: IAstNode;
const EvaluatorFactory: TEvaluatorFactory
@@ -119,7 +93,7 @@ uses
System.Generics.Defaults,
System.SyncObjs,
Myc.Ast.Compiler.Binder,
- Myc.Ast.Evaluator, // Required for TEvaluatorVisitor.Execute
+ Myc.Ast.Evaluator,
Myc.Data.Keyword;
{ TExpansionVisitor }
@@ -129,41 +103,23 @@ begin
inherited Create;
FMacroScope := AMacroScope;
FEvaluate := AEvaluate;
-
- // [*] Initialize hygiene tools
FRenameMap := TDictionary.Create;
-
- // Create a descriptor for the *template* scope, parented to nil.
- // It only tracks template-internal definitions for shadowing.
- FDefinitionDescriptor := TScope.CreateDescriptor(nil);
end;
destructor TExpansionVisitor.Destroy;
begin
FRenameMap.Free;
- // FDefinitionDescriptor is an interface
inherited;
end;
-procedure TExpansionVisitor.EnterScope;
-begin
- // [*] Create a new nested definition scope
- FDefinitionDescriptor := TScope.CreateDescriptor(FDefinitionDescriptor);
-end;
-
-procedure TExpansionVisitor.ExitScope;
-begin
- // [*] Revert to parent definition scope
- FDefinitionDescriptor := FDefinitionDescriptor.Parent;
-end;
-
function TExpansionVisitor.Gensym(const ABaseName: string): string;
begin
if not FRenameMap.TryGetValue(ABaseName, Result) then
begin
var count := TInterlocked.Add(FGensymCounter, 1);
- inc(count);
- Result := ABaseName + '_' + count.ToString;
+ if count < 0 then
+ count := -count;
+ Result := ABaseName + '#' + count.ToString;
FRenameMap.Add(ABaseName, Result);
end;
end;
@@ -176,7 +132,7 @@ class function TExpansionVisitor.Expand(
begin
var expander := TExpansionVisitor.Create(MacroScope, AEvaluate);
try
- Result := expander.Accept(RootNode); // Use IAstNode-returning Accept
+ Result := expander.Accept(RootNode);
finally
expander.Free;
end;
@@ -195,7 +151,6 @@ begin
if node.Kind = akUnquoteSplicing then
begin
var spliceExpr := node.AsUnquoteSplicing.Expression;
- // Evaluate the unquoted expression
var evaluatedSpliceValue := FEvaluate(spliceExpr);
if (not evaluatedSpliceValue.IsVoid) and (evaluatedSpliceValue.Kind = vkInterface) then
@@ -204,19 +159,15 @@ begin
if nodeToSplice.Kind = akBlockExpression then
newList.AddRange(nodeToSplice.AsBlockExpression.Expressions)
else
- // Allow splicing single nodes, not just blocks
newList.Add(nodeToSplice);
end
else if (not evaluatedSpliceValue.IsVoid) then
begin
- // If the value is not an AST node (e.g. a scalar), wrap it in a constant
newList.Add(TAst.Constant(evaluatedSpliceValue));
end;
- // else (if void, splice nothing)
end
else
begin
- // [*] Use the IAstNode-returning Accept helper (hygienic transform)
transformedNode := Self.Accept(node);
if Assigned(transformedNode) then
newList.Add(transformedNode);
@@ -232,20 +183,11 @@ function TExpansionVisitor.VisitIdentifier(const Node: IIdentifierNode): IAstNod
var
newName: string;
begin
- // [*] Hygiene Rule 1: Check for local rename
- // Is this symbol defined *within the template* (and thus renamed)?
if FRenameMap.TryGetValue(Node.Name, newName) then
begin
- // Yes, replace it with the hygienic name (e.g. 'start-time$1')
- // We use TTypes.Unknown because the Binder/TypeChecker run *after* this.
Result := TAst.Identifier(newName, TTypes.Unknown);
exit;
end;
-
- // [*] Hygiene Rule 2 & 3: Free Symbol (Hygienic 'print' or Unhygienic '*debug-mode*')
- // No, it's a free symbol. Leave it as-is.
- // The TAstBinder will resolve it later, either in the
- // macro's definition scope (Rule 2) or the caller's scope (Rule 3).
Result := Node;
end;
@@ -255,20 +197,9 @@ var
newIdent: IIdentifierNode;
newInit: IAstNode;
begin
- // [*] Hygiene Rule 1: This is a definition within the template.
-
- // 1. Visit initializer *before* defining the var
- // (so initializer can't see the var it's defining)
newInit := Accept(Node.Initializer);
-
- // 2. Generate hygienic name
newName := Gensym(Node.Identifier.Name);
- FDefinitionDescriptor.Define(newName);
-
- // 3. Create new identifier node (type is unknown)
newIdent := TAst.Identifier(newName, TTypes.Unknown);
-
- // 4. Create new VarDecl node
Result := TAst.VarDecl(newIdent, newInit, TTypes.Unknown);
end;
@@ -279,82 +210,58 @@ var
newName: string;
i: Integer;
begin
- // [*] Hygiene Rule 1: Lambda parameters are definitions.
-
- // 1. Enter a new definition scope for the template
- EnterScope;
- try
- // 2. Rename parameters
- SetLength(newParams, Length(Node.Parameters));
- for i := 0 to High(Node.Parameters) do
- begin
- newName := Gensym(Node.Parameters[i].Name);
- FDefinitionDescriptor.Define(newName);
- newParams[i] := TAst.Identifier(newName, TTypes.Unknown);
- end;
-
- // 3. Visit body recursively in the new scope
- newBody := Accept(Node.Body);
-
- finally
- // 4. Exit definition scope
- ExitScope;
+ SetLength(newParams, Length(Node.Parameters));
+ for i := 0 to High(Node.Parameters) do
+ begin
+ newName := Gensym(Node.Parameters[i].Name);
+ newParams[i] := TAst.Identifier(newName, TTypes.Unknown);
end;
- // 5. Create new Lambda node (type is unknown)
- Result := TAst.LambdaExpr(newParams, newBody, nil, nil, False, TTypes.Unknown);
+ newBody := Accept(Node.Body);
+
+ Result := TAst.LambdaExpr(newParams, newBody, nil, nil, nil, False, TTypes.Unknown);
end;
function TExpansionVisitor.VisitUnquote(const Node: IUnquoteNode): IAstNode;
var
value: TDataValue;
expr: IAstNode;
- symbol: TResolvedSymbol;
+ addr: TResolvedAddress;
begin
- // [*] This node *stops* the hygienic transformation.
- // It reverts to the previous compile-time evaluation logic.
-
expr := Node.Expression;
- // Check if we are unquoting a macro parameter (simple identifier)
if expr.Kind = akIdentifier then
begin
- symbol := FMacroScope.Descriptor.FindSymbol(expr.AsIdentifier.Name);
- if (symbol.Address.Kind = akLocalOrParent) and (symbol.Address.ScopeDepth = 0) then
+ addr := FMacroScope.Resolve(expr.AsIdentifier.Name);
+
+ if (addr.Kind = akLocalOrParent) and (addr.ScopeDepth = 0) then
begin
- // It's a parameter. Return the TDataValue containing the AST node passed as argument.
- var argValue := FMacroScope.Values[symbol.Address];
+ var argValue := FMacroScope.Values[addr];
if argValue.Kind = vkInterface then
begin
- Result := argValue.AsIntf; // Return the node directly
+ Result := argValue.AsIntf;
exit;
end;
end;
end;
- // If not a simple parameter, evaluate the expression at compile-time.
value := FEvaluate(expr);
- // If the result is an AST node (e.g. from a helper function), return it directly.
if value.Kind = vkInterface then
begin
Result := value.AsIntf;
exit;
end;
- // If the result is a scalar, text, or void, wrap it in a TConstantNode.
if value.Kind in [vkScalar, vkText, vkVoid] then
- begin
- Result := TAst.Constant(value);
- end
+ Result := TAst.Constant(value)
else
raise Exception.CreateFmt('Cannot unquote complex runtime value of type %s at compile time.', [value.Kind.ToString]);
end;
function TExpansionVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
begin
- // This should be handled by the caller (VisitFunctionCall / VisitBlockExpression)
- raise Exception.Create('Unquote-splicing (`~@`) can only appear inside a list form (e.g., a function call or a `do` block).');
+ raise Exception.Create('Unquote-splicing (`~@`) can only appear inside a list form.');
end;
function TExpansionVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
@@ -362,9 +269,7 @@ var
newArgs: TArray;
transformedCallee: IAstNode;
begin
- // [*] Callee must be transformed hygienically
- transformedCallee := Self.Accept(Node.Callee); // Calls IAstNode helper
- // [*] Arguments must be transformed and allow splicing
+ transformedCallee := Self.Accept(Node.Callee);
newArgs := TransformAndSpliceNodes(Node.Arguments);
Result := TAst.FunctionCall(transformedCallee, newArgs);
end;
@@ -373,16 +278,12 @@ function TExpansionVisitor.VisitBlockExpression(const Node: IBlockExpressionNode
var
newExprs: TArray;
begin
- // [*] Expressions must be transformed and allow splicing
newExprs := TransformAndSpliceNodes(Node.Expressions);
Result := TAst.Block(newExprs);
end;
function TExpansionVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
begin
- // Record literals do not support splicing.
- // We just use the default TAstTransformer implementation which visits child values.
- // [*] This will correctly call VisitIdentifier for keys/values.
Result := inherited VisitRecordLiteral(Node);
end;
@@ -395,33 +296,23 @@ constructor TMacroExpander.Create(
);
begin
inherited Create;
- Assert(Assigned(ARootRegistry));
- Assert(Assigned(AInitialScope));
- Assert(Assigned(AEvaluatorFactory));
-
FInitialScope := AInitialScope;
FEvaluatorFactory := AEvaluatorFactory;
-
- // Creates the root registry for this specific compilation run,
- // parented to the global registry from the environment.
FCurrentMacroRegistry := ARootRegistry.CreateChildRegistry;
end;
destructor TMacroExpander.Destroy;
begin
- // FCurrentMacroRegistry is an interface, managed by ARC
inherited Destroy;
end;
procedure TMacroExpander.EnterMacroScope;
begin
- // Creates a new registry with the current as Parent
FCurrentMacroRegistry := FCurrentMacroRegistry.CreateChildRegistry;
end;
procedure TMacroExpander.ExitMacroScope;
begin
- // Revert to parent registry. ARC will free the old child.
FCurrentMacroRegistry := FCurrentMacroRegistry.Parent;
end;
@@ -432,18 +323,15 @@ class function TMacroExpander.ExpandMacros(
const EvaluatorFactory: TEvaluatorFactory
): IAstNode;
begin
- // IAstMacroExpander.Execute gibt keinen Descriptor mehr zurueck
var expander := TMacroExpander.Create(ARootRegistry, AInitialScope, EvaluatorFactory) as IAstMacroExpander;
Result := expander.Execute(RootNode);
end;
function TMacroExpander.Execute(const RootNode: IAstNode): IAstNode;
begin
- // This executes the transformation (Accept)
- Result := Accept(RootNode); // Calls the IAstNode-returning helper
-
+ Result := Accept(RootNode);
if not Assigned(Result) then
- Result := TAst.Block([]); // e.g. if root was (defmacro ...)
+ Result := TAst.Block([]);
end;
function TMacroExpander.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
@@ -468,10 +356,7 @@ end;
function TMacroExpander.VisitMacroDefinition(const Node: IMacroDefinitionNode): IAstNode;
begin
- // Register the macro in the *current* macro registry
FCurrentMacroRegistry.Define(Node);
-
- // Keep the node in the AST (for the visualizer).
Result := Node;
end;
@@ -481,82 +366,76 @@ var
macroDef: IMacroDefinitionNode;
i: Integer;
begin
- // Check if this is an identifier
if Node.Callee.Kind <> akIdentifier then
exit(inherited VisitFunctionCall(Node));
calleeIdentifier := Node.Callee.AsIdentifier;
-
- // Check if this identifier is a macro in the current registry
macroDef := FCurrentMacroRegistry.Find(calleeIdentifier.Name);
if macroDef = nil then
- exit(inherited VisitFunctionCall(Node)); // Not a macro, let transformer continue
+ exit(inherited VisitFunctionCall(Node));
- // --- It is a macro, expand it ---
+ // --- Macro Expansion ---
- // 1. Create the temporary scope for the macro parameters
- // It must be parented to the *initial* scope to find other globals.
+ // 1. Create temporary scope for macro arguments (parented to Initial/Global Scope)
var expansionScope := TScope.CreateScope(FInitialScope, nil, nil);
+
var params := macroDef.Parameters;
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)]);
+ raise Exception.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, Length(params)]);
- // 2. Populate scope: Map parameter names to the *un-evaluated* AST nodes
+ // 2. Populate scope
for i := 0 to High(params) do
- expansionScope.Define(params[i].Name, TDataValue.FromIntf(Node.Arguments[i])); // Use FromIntf
+ expansionScope.Define(params[i].Name, TDataValue.FromIntf(Node.Arguments[i]));
- // 3. Create the evaluation callback (TEvaluateProc)
- // It must use the FInitialScope (for globals) and the expansionScope (for args)
+ // 3. Create Evaluator Callback
var evaluatorProc: TEvaluateProc;
evaluatorProc :=
function(const ANodeToEvaluate: IAstNode): TDataValue
var
- subDescriptor: IScopeDescriptor;
- evalScope: IExecutionScope;
+ tmpLayout: IScopeLayout;
+ scratchScope: IExecutionScope;
evaluator: IEvaluatorVisitor;
boundSubAst: IAstNode;
begin
- // This is the compile-time evaluation (Binder + Evaluator)
- // [*] It must bind against the *expansion* scope, not the initial scope
- boundSubAst := TAstBinder.Bind(expansionScope.Descriptor, ANodeToEvaluate, subDescriptor);
+ // A. BINDING
+ // The Binder creates a virtual child scope builder.
+ // Symbols in expansionScope will have ScopeDepth = 1.
+ // We access the layout via the descriptor (which for dynamic scopes is created on demand/proxy).
+ // Note: expansionScope is dynamic, so its Descriptor.Layout reflects current variables.
+ boundSubAst := TAstBinder.Bind(expansionScope.Descriptor.Layout, ANodeToEvaluate, tmpLayout);
- // Create eval scope from the new descriptor, parented to the expansion scope
- evalScope := subDescriptor.CreateScope(expansionScope);
+ // B. SCOPE MATCHING (The Fix)
+ // We must execute in a child scope so that ScopeDepth = 1 correctly points to expansionScope.
+ scratchScope := TScope.CreateScope(expansionScope, nil, nil);
- evaluator := FEvaluatorFactory(evalScope);
+ // C. EXECUTION
+ evaluator := FEvaluatorFactory(scratchScope);
Result := evaluator.Execute(boundSubAst);
end;
- // 4. Expand the macro body using the TExpansionVisitor
- // [*] FInitialScope removed from call
+ // 4. Expand Body
var expandedBody := TExpansionVisitor.Expand(expansionScope, macroDef.Body.AsQuasiquote.Expression, evaluatorProc);
- // 5. Wrap the result in a MacroExpansionNode (for debugging/tracing)
+ // 5. Wrap result
var macroNode := TAst.MacroExpansionNode(Node, expandedBody);
- // 6. IMPORTANT: Re-run the expander on the *new* AST fragment
- // to handle macros that expand into other macros.
- Result := Self.Accept(macroNode); // Calls the IAstNode helper, returns IAstNode
+ // 6. Re-expand result
+ Result := Self.Accept(macroNode);
end;
function TMacroExpander.VisitQuasiquote(const Node: IQuasiquoteNode): IAstNode;
begin
- // [*] Do not traverse into nested quasiquotes.
- // TExpansionVisitor handles them when (if) they are unquoted.
Result := Node;
end;
function TMacroExpander.VisitUnquote(const Node: IUnquoteNode): IAstNode;
begin
- // [*] Standalone unquote (outside a quasiquote) is a syntax error.
- raise Exception.Create('Unquote (`~`) can only be used inside a quasiquote (` `) template.');
+ raise Exception.Create('Unquote (`~`) can only be used inside a quasiquote.');
end;
function TMacroExpander.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode;
begin
- // [*] Standalone unquote-splicing (outside a quasiquote) is a syntax error.
- raise Exception.Create('Unquote-splicing (`~@`) can only be used inside a quasiquote (` `) template.');
+ raise Exception.Create('Unquote-splicing (`~@`) can only be used inside a quasiquote.');
end;
end.
diff --git a/Src/AST/Myc.Ast.Compiler.Specializer.pas b/Src/AST/Myc.Ast.Compiler.Specializer.pas
index d6f4a29..f75c3f5 100644
--- a/Src/AST/Myc.Ast.Compiler.Specializer.pas
+++ b/Src/AST/Myc.Ast.Compiler.Specializer.pas
@@ -19,7 +19,7 @@ type
function Execute(const RootNode: IAstNode): IAstNode;
end;
- // This transformer runs *after* TypeChecker
+ // 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.
TStaticSpecializer = class(TAstTransformer, IAstSpecializer)
@@ -35,11 +35,8 @@ type
constructor Create(const AEnvironment: IEnvironment);
function Execute(const RootNode: IAstNode): IAstNode;
- class function Specialize(
- const AEnvironment: IEnvironment;
- const RootNode: IAstNode;
- const ADescriptor: IScopeDescriptor
- ): IAstNode; static;
+ // Descriptor removed from signature as it is not needed for specialization
+ class function Specialize(const AEnvironment: IEnvironment; const RootNode: IAstNode): IAstNode; static;
end;
implementation
@@ -53,13 +50,8 @@ begin
FEnvironment := AEnvironment;
end;
-class function TStaticSpecializer.Specialize(
- const AEnvironment: IEnvironment;
- const RootNode: IAstNode;
- const ADescriptor: IScopeDescriptor
-): IAstNode;
+class function TStaticSpecializer.Specialize(const AEnvironment: IEnvironment; const RootNode: IAstNode): IAstNode;
begin
- // ADescriptor is no longer needed here, the environment has all it needs
var specializer := TStaticSpecializer.Create(AEnvironment) as IAstSpecializer;
Result := specializer.Execute(RootNode);
end;
@@ -73,7 +65,6 @@ end;
function TStaticSpecializer.GetStaticRtlFunction(const AName: string; const AArgTypes: TArray): TSpecializedMethod;
begin
- // Helper to query the RTL bootstrap cache
Result := TRtlRegistry.GetStaticSpecialization(AName, AArgTypes);
end;
@@ -136,15 +127,8 @@ begin
if FEnvironment.MonomorphCache.TryGetValue(key, specializedMethod) then
begin
- // 4a. Cache Hit (Environment): Found user-defined or previously specialized fn
- Result :=
- TAst.FunctionCall(
- newCallee,
- newArgs,
- specializedMethod.ReturnType, // Use the type from the cache
- Node.IsTailCall,
- specializedMethod.Target // Use the TFunc from the cache
- );
+ // 4a. Cache Hit (Environment)
+ Result := TAst.FunctionCall(newCallee, newArgs, specializedMethod.ReturnType, Node.IsTailCall, specializedMethod.Target);
exit;
end;
@@ -152,17 +136,10 @@ begin
specializedMethod := GetStaticRtlFunction(funcName, argTypes);
if Assigned(specializedMethod.Target) then
begin
- // 5a. Cache Hit (RTL): Found a native, static RTL function
+ // 5a. Cache Hit (RTL)
FEnvironment.MonomorphCache.Add(key, specializedMethod);
- Result :=
- TAst.FunctionCall(
- newCallee,
- newArgs,
- specializedMethod.ReturnType, // Use the type from the cache
- Node.IsTailCall,
- specializedMethod.Target // Use the TFunc from the cache
- );
+ Result := TAst.FunctionCall(newCallee, newArgs, specializedMethod.ReturnType, Node.IsTailCall, specializedMethod.Target);
exit;
end;
@@ -171,22 +148,19 @@ begin
if (funcDef <> nil) then
begin
- // Cannot specialize closures or functions with nested closures
- // that might depend on the local environment being detached.
- // We check if it's a lambda expression node (which it usually is)
+ // Cannot specialize closures safely without more complex analysis
if funcDef.Kind = akLambdaExpression then
begin
var lambdaDef := funcDef.AsLambdaExpression;
if (Length(lambdaDef.Upvalues) > 0) or (lambdaDef.HasNestedLambdas) then
begin
- // This function relies on local scope context. We cannot lift it
- // to a static compilation unit safely. Fallback to dynamic dispatch.
Result := TAst.FunctionCall(newCallee, newArgs, Node.StaticType, Node.IsTailCall, nil);
exit;
end;
end;
// 6a. Compile func with KNOWN TYPES
+ // This recursively triggers Bind -> Check -> Specialize for the callee body!
var compiled := FEnvironment.Compile(funcDef, argTypes);
// 6b. Store in cache
@@ -195,19 +169,12 @@ begin
specializedMethod := TSpecializedMethod.Create(compiled.Func, returnType);
FEnvironment.MonomorphCache.Add(key, specializedMethod);
- // 6e. Return the new node
- Result :=
- TAst.FunctionCall(
- newCallee,
- newArgs,
- returnType, // Use the extracted return type
- Node.IsTailCall,
- compiled.Func // Use the compiled TFunc
- );
+ // 6c. Return the new node
+ Result := TAst.FunctionCall(newCallee, newArgs, returnType, Node.IsTailCall, compiled.Func);
exit;
end;
- // 7. Fallback: Not RTL, Not User-Code (oder AST nicht gefunden) -> Dynamic
+ // 7. Fallback: Not RTL, Not User-Code -> Dynamic
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then
Result := Node
else
diff --git a/Src/AST/Myc.Ast.Compiler.TCO.pas b/Src/AST/Myc.Ast.Compiler.TCO.pas
index 964e000..f0f8154 100644
--- a/Src/AST/Myc.Ast.Compiler.TCO.pas
+++ b/Src/AST/Myc.Ast.Compiler.TCO.pas
@@ -18,7 +18,7 @@ type
function Execute(const RootNode: IAstNode): IAstNode;
end;
- // This transformer runs *after* the Lowerer (Phase 4).
+ // This transformer runs *after* the Lowerer (Phase 4/5) and Specializer.
// Its sole responsibility is to identify tail calls (TCO)
// and set the `IsTailCall` flag on TFunctionCallNode.
TAstTCO = class(TAstTransformer, IAstTCO)
@@ -70,7 +70,7 @@ end;
function TAstTCO.Execute(const RootNode: IAstNode): IAstNode;
begin
- Result := Accept(RootNode); // Use IAstNode-returning Accept
+ Result := Accept(RootNode);
if not Assigned(Result) then
Result := TAst.Block([]);
end;
@@ -85,7 +85,7 @@ begin
FIsTailStack.Push(FNextIsTail);
try
- // Call inherited Accept, which handles the data unwrapping
+ // Call inherited Accept
Result := inherited Accept(Node);
finally
FNextIsTail := FIsTailStack.Pop;
@@ -105,6 +105,7 @@ begin
Node.Expressions,
function(idx: Integer; Node: IAstNode): IAstNode
begin
+ // Only the last expression in the block inherits the tail position status
FNextIsTail := isContextTail and (idx = nTail);
Result := Accept(Node);
end
@@ -165,11 +166,11 @@ var
newParams: TArray;
newBody: IAstNode;
begin
- // Parameters are not in tail position (handled by AcceptParameters)
+ // Parameters are not in tail position
FNextIsTail := False;
newParams := AcceptParameters(Node.Parameters);
- // The body of a lambda is *always* a tail position (relative to the lambda)
+ // The body of a lambda is *always* a tail position (relative to the lambda execution)
FNextIsTail := True;
newBody := Accept(Node.Body);
@@ -177,8 +178,9 @@ begin
Result := Node
else
begin
- // Rebuild using factory and copy properties via interface
- Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
+ // Rebuild using factory.
+ // IMPORTANT: Pass Layout AND Descriptor (TCO runs after TypeChecker).
+ Result := TAst.LambdaExpr(newParams, newBody, Node.Layout, Node.Descriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
end;
end;
@@ -210,7 +212,6 @@ begin
if newBody = Node.ExpandedBody then
Result := Node
else
- // Rebuild, preserving the original CallNode metadata
Result := TAst.MacroExpansionNode(Node.CallNode, newBody);
end;
@@ -222,13 +223,12 @@ var
begin
isTailCall := FIsTailStack.Peek;
- // Arguments are not in tail position
+ // Callee/Arguments are not in tail position
FNextIsTail := False;
newCallee := Accept(Node.Callee);
newArgs := AcceptNodes(Node.Arguments);
// CoW check: Create a new node only if children changed OR IsTailCall needs update
- // OR StaticTarget is missing (which it shouldn't be, but good to check)
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) and (isTailCall = Node.IsTailCall) then
begin
Result := Node;
@@ -242,7 +242,7 @@ begin
newArgs,
Node.StaticType,
isTailCall,
- Node.StaticTarget // Preserve the static target
+ Node.StaticTarget // Preserve the static target from Specializer
);
end;
diff --git a/Src/AST/Myc.Ast.Compiler.TypeChecker.pas b/Src/AST/Myc.Ast.Compiler.TypeChecker.pas
index b5340ee..479d9d7 100644
--- a/Src/AST/Myc.Ast.Compiler.TypeChecker.pas
+++ b/Src/AST/Myc.Ast.Compiler.TypeChecker.pas
@@ -16,16 +16,32 @@ uses
type
IAstTypeChecker = interface(IAstVisitor)
- function Execute(const RootNode: IAstNode; const ADecriptor: IScopeDescriptor): IAstNode;
+ function Execute(const RootNode: IAstNode; const Layout: IScopeLayout): IAstNode;
end;
- // This transformer runs *after* the TAstBinder.
- // It takes the "Bound AST" (which has addresses but mostly TTypes.Unknown)
- // and traverses it bottom-up to infer and check all static types.
- // It *replaces* all IAstTypedNodes with new nodes containing the correct type.
TTypeChecker = class(TAstTransformer, IAstTypeChecker)
private
- FCurrentDescriptor: IScopeDescriptor;
+ type
+ // Helper to track types per scope during traversal (The "Scratchpad")
+ TTypeContext = class
+ private
+ FParent: TTypeContext;
+ FLayout: IScopeLayout;
+ FSlotTypes: TArray;
+ FUpvalueTypes: TArray; // Types of captured variables
+ public
+ constructor Create(AParent: TTypeContext; ALayout: IScopeLayout; const AUpvalueTypes: TArray);
+ function LookupType(const Address: TResolvedAddress): IStaticType;
+ procedure SetType(SlotIndex: Integer; AType: IStaticType);
+ property Types: TArray read FSlotTypes;
+ end;
+
+ private
+ FCurrentContext: TTypeContext;
+
+ // Helper to recursively rebuild the context stack from the layout hierarchy
+ function CreateContextChain(L: IScopeLayout): TTypeContext;
+
protected
// Override all visit methods to perform type checking
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
@@ -50,10 +66,12 @@ type
function VisitKeyword(const Node: IKeywordNode): IAstNode; override;
public
- constructor Create(const ADescriptor: IScopeDescriptor);
- function Execute(const RootNode: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode;
+ constructor Create(const RootLayout: IScopeLayout);
+ destructor Destroy; override;
- class function CheckTypes(const RootNode: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode; static;
+ function Execute(const RootNode: IAstNode; const Layout: IScopeLayout): IAstNode;
+
+ class function CheckTypes(const RootNode: IAstNode; const Layout: IScopeLayout): IAstNode; static;
end;
implementation
@@ -62,58 +80,143 @@ uses
System.Generics.Defaults,
Myc.Data.Keyword;
-{ TTypeChecker }
+{ TTypeChecker.TTypeContext }
-constructor TTypeChecker.Create(const ADescriptor: IScopeDescriptor);
+constructor TTypeChecker.TTypeContext.Create(AParent: TTypeContext; ALayout: IScopeLayout; const AUpvalueTypes: TArray);
begin
inherited Create;
- Assert(Assigned(ADescriptor));
- FCurrentDescriptor := ADescriptor;
+ FParent := AParent;
+ FLayout := ALayout;
+ FUpvalueTypes := AUpvalueTypes;
+
+ // Initialize slot types with Unknown
+ if Assigned(FLayout) then
+ begin
+ SetLength(FSlotTypes, FLayout.SlotCount);
+ for var i := 0 to High(FSlotTypes) do
+ FSlotTypes[i] := TTypes.Unknown;
+ end;
end;
-class function TTypeChecker.CheckTypes(const RootNode: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode;
+function TTypeChecker.TTypeContext.LookupType(const Address: TResolvedAddress): IStaticType;
+var
+ ctx: TTypeContext;
+ i: Integer;
begin
- var checker := TTypeChecker.Create(ADescriptor) as IAstTypeChecker;
- Result := checker.Execute(RootNode, ADescriptor);
+ case Address.Kind of
+ akLocalOrParent:
+ begin
+ ctx := Self;
+ for i := 1 to Address.ScopeDepth do
+ begin
+ if not Assigned(ctx.FParent) then
+ raise Exception.CreateFmt('Scope depth mismatch during type lookup. Requested Depth: %d.', [Address.ScopeDepth]);
+ ctx := ctx.FParent;
+ end;
+
+ if (Address.SlotIndex >= 0) and (Address.SlotIndex < Length(ctx.FSlotTypes)) then
+ Result := ctx.FSlotTypes[Address.SlotIndex]
+ else
+ Result := TTypes.Unknown;
+ end;
+ akUpvalue:
+ begin
+ // Look up type in our local upvalue type cache
+ if (Address.SlotIndex >= 0) and (Address.SlotIndex < Length(FUpvalueTypes)) then
+ Result := FUpvalueTypes[Address.SlotIndex]
+ else
+ Result := TTypes.Unknown;
+ end;
+ else
+ Result := TTypes.Unknown;
+ end;
end;
-function TTypeChecker.Execute(const RootNode: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode;
+procedure TTypeChecker.TTypeContext.SetType(SlotIndex: Integer; AType: IStaticType);
begin
- FCurrentDescriptor := ADescriptor;
- Result := Accept(RootNode); // Use IAstNode-returning Accept
+ if (SlotIndex >= 0) and (SlotIndex < Length(FSlotTypes)) then
+ FSlotTypes[SlotIndex] := AType;
+end;
+
+{ TTypeChecker }
+
+function TTypeChecker.CreateContextChain(L: IScopeLayout): TTypeContext;
+var
+ p: TTypeContext;
+begin
+ if L = nil then
+ exit(nil);
+
+ // Recursively build parent context
+ p := CreateContextChain(L.Parent);
+
+ // Create context for current layout level
+ // Note: We don't know UpvalueTypes for these static/parent layouts here, so [] is passed.
+ // Also: Slot types will be initialized to Unknown.
+ Result := TTypeContext.Create(p, L, []);
+end;
+
+constructor TTypeChecker.Create(const RootLayout: IScopeLayout);
+begin
+ inherited Create;
+ // Build the full context chain based on the Layout's parent hierarchy
+ // This ensures that ScopeDepth lookups can traverse up to the root.
+ FCurrentContext := CreateContextChain(RootLayout);
+
+ // Fallback if RootLayout is nil (should not happen in valid pipeline)
+ if FCurrentContext = nil then
+ FCurrentContext := TTypeContext.Create(nil, nil, []);
+end;
+
+destructor TTypeChecker.Destroy;
+begin
+ // Cleanup context stack
+ while Assigned(FCurrentContext) do
+ begin
+ var temp := FCurrentContext;
+ FCurrentContext := FCurrentContext.FParent;
+ temp.Free;
+ end;
+ inherited;
+end;
+
+class function TTypeChecker.CheckTypes(const RootNode: IAstNode; const Layout: IScopeLayout): IAstNode;
+begin
+ var checker := TTypeChecker.Create(Layout) as IAstTypeChecker;
+ Result := checker.Execute(RootNode, Layout);
+end;
+
+function TTypeChecker.Execute(const RootNode: IAstNode; const Layout: IScopeLayout): IAstNode;
+begin
+ // Note: Layout passed here matches what was passed to Create/Constructor (via recursed ContextChain)
+ Result := Accept(RootNode);
if not Assigned(Result) then
Result := TAst.Block([]);
end;
function TTypeChecker.VisitConstant(const Node: IConstantNode): IAstNode;
begin
- // This is a leaf node.
- // If the constructor couldn't set the type, nobody can
Assert(Node.StaticType.Kind <> stUnknown);
Result := Node;
end;
function TTypeChecker.VisitKeyword(const Node: IKeywordNode): IAstNode;
begin
- // This is a leaf node.
- // The TKeywordNode constructor *forces* the type to be TTypes.Keyword.
Assert(Node.StaticType.Kind = stKeyword);
Result := Node;
end;
function TTypeChecker.VisitIdentifier(const Node: IIdentifierNode): IAstNode;
var
- symbol: TResolvedSymbol;
+ typ: IStaticType;
adr: TResolvedAddress;
begin
- // This is a leaf node (guaranteed to be IBoundIdentifierNode by Binder)
-
- // Get the type from the descriptor (which was populated by Binder/RTL)
- symbol := FCurrentDescriptor.FindSymbol(Node.Name);
adr := Node.Address;
+ // Lookup type in our scratchpad context using the address resolved by Binder
+ typ := FCurrentContext.LookupType(adr);
- // Create a new node, copying the address and assigning the inferred type
- Result := TAst.Identifier(Node.Name, adr, symbol.StaticType);
+ // Create a new node with the inferred type
+ Result := TAst.Identifier(Node.Name, adr, typ);
end;
function TTypeChecker.VisitRecurNode(const Node: IRecurNode): IAstNode;
@@ -121,12 +224,10 @@ var
newArgs: TArray;
i: Integer;
begin
- // 1. Visit children
SetLength(newArgs, Length(Node.Arguments));
for i := 0 to High(Node.Arguments) do
newArgs[i] := Accept(Node.Arguments[i]);
- // 2. Create new node with inferred type
Result := TAst.Recur(newArgs, TTypes.Void);
end;
@@ -139,57 +240,43 @@ var
placeholderType: IStaticType;
i: Integer;
begin
- // 1. Get the address from the bound identifier (Binder did this)
adr := Node.Identifier.Address;
- initType := TTypes.Unknown; // Default
+ initType := TTypes.Unknown;
- // 2. Check for recursive lambda and bootstrap the type
+ // Recursive lambda bootstrap logic
placeholderType := nil;
if (Node.Initializer <> nil) and (Node.Initializer.Kind = akLambdaExpression) then
begin
lambdaNode := Node.Initializer.AsLambdaExpression;
-
- // Create a placeholder method type based on the *unvisited* lambda's parameter *count*.
var paramTypes: TArray;
SetLength(paramTypes, Length(lambdaNode.Parameters));
for i := 0 to High(paramTypes) do
paramTypes[i] := TTypes.Unknown;
- // Create the placeholder (Return type is also Unknown for now)
placeholderType := TTypes.CreateMethod(paramTypes, TTypes.Unknown);
- // 3. *Update the descriptor* with the placeholder *before* visiting the initializer
- FCurrentDescriptor.UpdateType(adr.SlotIndex, placeholderType);
- initType := placeholderType; // Store this
+ // Update Context (Scratchpad)
+ FCurrentContext.SetType(adr.SlotIndex, placeholderType);
+ initType := placeholderType;
end;
- // 4. Visit Initializer (if it exists)
if Assigned(Node.Initializer) then
newInitializer := Accept(Node.Initializer)
else
newInitializer := nil;
- // 5. Get the *final* inferred initializer type
if Assigned(newInitializer) then
initType := newInitializer.AsTypedNode.StaticType
- else if not Assigned(placeholderType) then // only if not already set
- initType := TTypes.Unknown; // (def f) - no initializer
+ else if not Assigned(placeholderType) then
+ initType := TTypes.Unknown;
- // 6. *Re-update* the type in the scope descriptor with the final, inferred type.
+ // Update Context with final type
if initType.Kind <> stUnknown then
- FCurrentDescriptor.UpdateType(adr.SlotIndex, initType);
+ FCurrentContext.SetType(adr.SlotIndex, initType);
- // 7. Create the new (typed) identifier node
newIdent := TAst.Identifier(Node.Identifier.Name, adr, initType);
- // 8. Create the new VariableDeclaration node using the factory
- Result :=
- TAst.VarDecl(
- newIdent.AsIdentifier,
- newInitializer,
- initType,
- Node.IsBoxed // 9. Copy runtime flags
- );
+ Result := TAst.VarDecl(newIdent.AsIdentifier, newInitializer, initType, Node.IsBoxed);
end;
function TTypeChecker.VisitAssignment(const Node: IAssignmentNode): IAstNode;
@@ -201,61 +288,49 @@ var
placeholderType: IStaticType;
i: Integer;
begin
- // 1. Visit Identifier *first* to get its address and current type
newIdent := Accept(Node.Identifier);
targetType := newIdent.AsTypedNode.StaticType;
adr := newIdent.AsIdentifier.Address;
- // 2. Check for recursive lambda assignment
+ // Recursive lambda assignment check
placeholderType := nil;
if (Node.Value <> nil) and (Node.Value.Kind = akLambdaExpression) then
begin
lambdaNode := Node.Value.AsLambdaExpression;
-
- // Create a placeholder (only if the target is not already a method type)
if (targetType.Kind <> stMethod) then
begin
var paramTypes: TArray;
SetLength(paramTypes, Length(lambdaNode.Parameters));
for i := 0 to High(paramTypes) do
- paramTypes[i] := TTypes.Unknown; // We infer param types later
+ paramTypes[i] := TTypes.Unknown;
placeholderType := TTypes.CreateMethod(paramTypes, TTypes.Unknown);
-
- // 3. *Update the descriptor* with the placeholder *before* visiting the value
- FCurrentDescriptor.UpdateType(adr.SlotIndex, placeholderType);
+ FCurrentContext.SetType(adr.SlotIndex, placeholderType);
targetType := placeholderType;
end;
end;
- // 4. Visit Value
newValue := Accept(Node.Value);
sourceType := newValue.AsTypedNode.StaticType;
- // 5. Check assignment
if not TTypeRules.CanAssign(targetType, sourceType) then
begin
- // If target was unknown, try promoting
if (targetType.Kind = stUnknown) then
begin
- if not TTypeRules.CanAssign(sourceType, targetType) then // Check reverse
+ if not TTypeRules.CanAssign(sourceType, targetType) then
raise ETypeException.CreateFmt('Cannot assign type %s to %s', [sourceType.ToString, targetType.ToString]);
end
else
raise ETypeException.CreateFmt('Cannot assign type %s to %s', [sourceType.ToString, targetType.ToString]);
end;
- // 6. If the target was 'Unknown' or a 'Placeholder', update the descriptor
- // with the new, final inferred type.
if ((targetType.Kind = stUnknown) or Assigned(placeholderType)) and (sourceType.Kind <> stUnknown) then
begin
- FCurrentDescriptor.UpdateType(adr.SlotIndex, sourceType);
- // Re-create the identifier node *with the new type*
+ FCurrentContext.SetType(adr.SlotIndex, sourceType);
newIdent := TAst.Identifier(newIdent.AsIdentifier.Name, adr, sourceType);
targetType := sourceType;
end;
- // 7. Create the new Assignment node
Result := TAst.Assign(newIdent.AsIdentifier, newValue, targetType);
end;
@@ -265,46 +340,67 @@ var
newBody: IAstNode;
bodyType, methodType: IStaticType;
paramTypes: TArray;
+
+ // Upvalue Type Resolution
+ upvalueTypes: TArray;
+ upvalueAddrs: TArray;
+
i: Integer;
- savedDescriptor: IScopeDescriptor;
+ finalDescriptor: IScopeDescriptor;
begin
- // 1. Enter the lambda's scope (which Binder already created)
- savedDescriptor := FCurrentDescriptor;
- FCurrentDescriptor := Node.ScopeDescriptor;
+ // 1. Resolve Upvalue Types *in the current (parent) context*
+ upvalueAddrs := Node.Upvalues;
+ SetLength(upvalueTypes, Length(upvalueAddrs));
+ for i := 0 to High(upvalueAddrs) do
+ begin
+ // We look up the physical address (from Binder) in the current context chain
+ upvalueTypes[i] := FCurrentContext.LookupType(upvalueAddrs[i]);
+ end;
+
+ // 2. Create new TypeContext for this lambda scope, PASSING the upvalue types
+ // We use the layout from the Binder-Result-Node
+ FCurrentContext := TTypeContext.Create(FCurrentContext, Node.Layout, upvalueTypes);
try
- // 2. Visit parameters (they are already bound, just need typing)
+ // 3. Set parameter types in the context
SetLength(newParams, Length(Node.Parameters));
SetLength(paramTypes, Length(Node.Parameters));
for i := 0 to High(Node.Parameters) do
begin
- // Parameters are leaves, but we must *replace* them with typed versions
- // (even if they are just TTypes.Unknown for now, for type inference placeholders)
var paramIdent := Node.Parameters[i];
var paramAdr := paramIdent.Address;
- var newParam := TAst.Identifier(paramIdent.Name, paramAdr);
- newParams[i] := newParam;
+ // Here we could infer types if we had type annotations. For now, parameters are Unknown.
paramTypes[i] := TTypes.Unknown;
+
+ // Update context so body can resolve params
+ FCurrentContext.SetType(paramAdr.SlotIndex, paramTypes[i]);
+
+ newParams[i] := TAst.Identifier(paramIdent.Name, paramAdr, paramTypes[i]);
end;
- // 3. Visit the body to infer its return type
+ // 4. Visit body
newBody := Accept(Node.Body);
bodyType := newBody.AsTypedNode.StaticType;
- // 4. Create the final method type
+ // 5. Create method type
methodType := TTypes.CreateMethod(paramTypes, bodyType);
- // 5. Update the type for (Slot 0) in the descriptor
- FCurrentDescriptor.UpdateType(0, methodType);
+ // 6. Update type in context (Slot 0)
+ FCurrentContext.SetType(0, methodType);
+
+ // 7. FINALIZE: Bake the Descriptor
+ finalDescriptor := TScope.CreateDescriptor(Node.Layout, FCurrentContext.Types);
finally
- // 6. Restore parent descriptor
- FCurrentDescriptor := savedDescriptor;
+ // 8. Pop Context
+ var temp := FCurrentContext;
+ FCurrentContext := FCurrentContext.FParent;
+ temp.Free;
end;
- // 7. Create the new (typed) lambda node using the factory
- Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, methodType);
+ // 9. Create new node with the Descriptor attached!
+ Result := TAst.LambdaExpr(newParams, newBody, Node.Layout, finalDescriptor, Node.Upvalues, Node.HasNestedLambdas, methodType);
end;
function TTypeChecker.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
@@ -319,7 +415,6 @@ var
sig: IMethodSignature;
match: Boolean;
begin
- // 1. Visit children first (bottom-up)
newCallee := Accept(Node.Callee);
SetLength(newArgs, Length(Node.Arguments));
SetLength(argTypes, Length(Node.Arguments));
@@ -333,53 +428,40 @@ begin
hasUnknownArgs := True;
end;
- // 2. Get callee type (now inferred)
calleeType := newCallee.AsTypedNode.StaticType;
- retType := TTypes.Unknown; // Default if not a method
+ retType := TTypes.Unknown;
- // 3. Perform type checking
if calleeType.Kind = TStaticTypeKind.stMethod then
begin
- // If any argument is Unknown, we cannot resolve overloads.
- // The return type remains Unknown.
if not hasUnknownArgs then
begin
bestSig := nil;
for sig in calleeType.Signatures do
begin
- // Check 1: Argument count
if Length(sig.ParamTypes) <> Length(argTypes) then
continue;
- // Check 2: Argument types (CanAssign)
match := True;
for j := 0 to High(argTypes) do
begin
if not TTypeRules.CanAssign(sig.ParamTypes[j], argTypes[j]) then
begin
match := False;
- break; // This signature doesn't match
+ break;
end;
end;
- // Check 3: Found first match
if match then
begin
- // This is the "dumb" checker logic: first match wins.
- // A "smarter" checker would find the *best* match.
bestSig := sig;
break;
end;
- end; // for sig
+ end;
- // Check 4: Handle results
if Assigned(bestSig) then
- begin
- retType := bestSig.ReturnType;
- end
+ retType := bestSig.ReturnType
else
begin
- // No signature matched, even with known types. This is an error.
var argsStr: string := '';
for i := 0 to High(argTypes) do
argsStr := argsStr + argTypes[i].ToString + ' ';
@@ -387,20 +469,11 @@ begin
.CreateFmt('No matching signature for call with args (%s) found on method %s', [argsStr, calleeType.ToString]);
end;
end;
- // else: hasUnknownArgs is True, so retType remains Unknown (as set in step 2)
end
else if calleeType.Kind <> TStaticTypeKind.stUnknown then
raise ETypeException.CreateFmt('Cannot invoke type %s as a function.', [calleeType.ToString]);
- // else: calleeType is Unknown (e.g. recursive call or unbound symbol), retType remains Unknown.
- // 4. Create the new (typed) call node using the factory
- Result :=
- TAst.FunctionCall(
- newCallee,
- newArgs,
- retType,
- Node.IsTailCall // 5. Copy runtime properties
- );
+ Result := TAst.FunctionCall(newCallee, newArgs, retType, Node.IsTailCall);
end;
function TTypeChecker.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
@@ -409,18 +482,15 @@ var
newExprs: TArray;
i: Integer;
begin
- // 1. Visit children
SetLength(newExprs, Length(Node.Expressions));
for i := 0 to High(Node.Expressions) do
newExprs[i] := Accept(Node.Expressions[i]);
- // 2. Type is type of last expression
if Length(newExprs) > 0 then
blockType := newExprs[High(newExprs)].AsTypedNode.StaticType
else
blockType := TTypes.Void;
- // 3. Create new node
Result := TAst.Block(newExprs, blockType);
end;
@@ -429,17 +499,14 @@ var
conditionType, thenType, elseType, resultType: IStaticType;
newCond, newThen, newElse: IAstNode;
begin
- // 1. Visit children
newCond := Accept(Node.Condition);
newThen := Accept(Node.ThenBranch);
- newElse := Accept(Node.ElseBranch); // Accept handles nil
+ newElse := Accept(Node.ElseBranch);
- // 2. Check condition
conditionType := newCond.AsTypedNode.StaticType;
if (conditionType.Kind <> stUnknown) and not TTypeRules.CanAssign(TTypes.Ordinal, conditionType) then
raise ETypeException.CreateFmt('If condition must be Ordinal, but got %s', [conditionType.ToString]);
- // 3. Promote branch types
thenType := newThen.AsTypedNode.StaticType;
elseType :=
if newElse <> nil then newElse.AsTypedNode.StaticType
@@ -447,7 +514,6 @@ begin
resultType := TTypeRules.Promote(thenType, elseType);
- // 4. Create new node
Result := TAst.IfExpr(newCond, newThen, newElse, resultType);
end;
@@ -456,23 +522,19 @@ var
conditionType, thenType, elseType, resultType: IStaticType;
newCond, newThen, newElse: IAstNode;
begin
- // 1. Visit children
newCond := Accept(Node.Condition);
newThen := Accept(Node.ThenBranch);
newElse := Accept(Node.ElseBranch);
- // 2. Check condition
conditionType := newCond.AsTypedNode.StaticType;
if (conditionType.Kind <> stUnknown) and not TTypeRules.CanAssign(TTypes.Ordinal, conditionType) then
raise ETypeException.CreateFmt('Ternary condition must be Ordinal, but got %s', [conditionType.ToString]);
- // 3. Promote branch types
thenType := newThen.AsTypedNode.StaticType;
elseType := newElse.AsTypedNode.StaticType;
resultType := TTypeRules.Promote(thenType, elseType);
- // 4. Create new node
Result := TAst.TernaryExpr(newCond, newThen, newElse, resultType);
end;
@@ -482,15 +544,12 @@ var
fieldIndex: Integer;
newBase, newMember: IAstNode;
begin
- // 1. Visit children
newBase := Accept(Node.Base);
- newMember := Accept(Node.Member); // Visits the TKeywordNode
+ newMember := Accept(Node.Member);
- // 2. Get types
baseType := newBase.AsTypedNode.StaticType;
elemType := TTypes.Unknown;
- // 3. Resolve
if (baseType.Kind <> TStaticTypeKind.stUnknown) then
begin
if (baseType.Kind = TStaticTypeKind.stRecord) or (baseType.Kind = TStaticTypeKind.stRecordSeries) then
@@ -503,7 +562,7 @@ begin
if baseType.Kind = TStaticTypeKind.stRecord then
elemType := fieldType
- else // stRecordSeries
+ else
elemType := TTypes.CreateSeries(fieldType);
end
else if (baseType.Kind = TStaticTypeKind.stGenericRecord) then
@@ -515,12 +574,9 @@ begin
elemType := genDef.Fields[fieldIndex].Value;
end
else
- begin
raise ETypeException.CreateFmt('Member access requires a record type, but got %s', [baseType.ToString]);
- end;
end;
- // 4. Create new node
Result := TAst.MemberAccess(newBase, newMember.AsKeyword, elemType);
end;
@@ -529,16 +585,13 @@ var
baseType, indexType, elemType: IStaticType;
newBase, newIndex: IAstNode;
begin
- // 1. Visit children
newBase := Accept(Node.Base);
newIndex := Accept(Node.Index);
- // 2. Get types
baseType := newBase.AsTypedNode.StaticType;
indexType := newIndex.AsTypedNode.StaticType;
elemType := TTypes.Unknown;
- // 3. Resolve
if (baseType.Kind <> TStaticTypeKind.stUnknown) then
begin
if (baseType.Kind <> TStaticTypeKind.stSeries) and (baseType.Kind <> TStaticTypeKind.stRecordSeries) then
@@ -549,11 +602,10 @@ begin
if baseType.Kind = TStaticTypeKind.stSeries then
elemType := baseType.ElementType
- else // stRecordSeries
+ else
elemType := TTypes.CreateRecord(baseType.Definition);
end;
- // 4. Create new node
Result := TAst.Indexer(newBase, newIndex, elemType);
end;
@@ -568,7 +620,6 @@ var
allScalar: Boolean;
newFields: TArray;
begin
- // 1. Visit all child nodes first to infer their types
SetLength(newFields, Length(Node.Fields));
for i := 0 to High(Node.Fields) do
begin
@@ -579,7 +630,6 @@ begin
SetLength(scalarDefFields, Length(newFields));
allScalar := True;
- // 2. Check if this record literal can be a TScalarRecord
for i := 0 to High(newFields) do
begin
valType := newFields[i].Value.AsTypedNode.StaticType;
@@ -593,14 +643,13 @@ begin
else
begin
allScalar := False;
- scalarKind := TScalar.TKind.Ordinal; // Dummy
+ scalarKind := TScalar.TKind.Ordinal;
end;
if allScalar then
scalarDefFields[i] := TScalarRecordField.Create(newFields[i].Key.Value, scalarKind);
end;
- // 3. Create the new node and set its type/definitions
if allScalar then
begin
def := TScalarRecordRegistry.Intern(scalarDefFields);
@@ -624,9 +673,6 @@ function TTypeChecker.VisitCreateSeries(const Node: ICreateSeriesNode): IAstNode
var
elemType: IStaticType;
begin
- // This is a leaf node
-
- // Assign the type
try
elemType := TTypes.FromScalarKind(TScalar.StringToKind(Node.Definition));
except
@@ -634,7 +680,6 @@ begin
elemType := TTypes.Unknown;
end;
- // Create new node
Result := TAst.CreateSeries(Node.Definition, TTypes.CreateSeries(elemType));
end;
@@ -643,16 +688,13 @@ var
seriesType, valueType: IStaticType;
newSeries, newValue, newLookback: IAstNode;
begin
- // 1. Visit children
newSeries := Accept(Node.Series);
newValue := Accept(Node.Value);
- newLookback := Accept(Node.Lookback); // Handles nil
+ newLookback := Accept(Node.Lookback);
- // 2. Get types
seriesType := newSeries.AsTypedNode.StaticType;
valueType := newValue.AsTypedNode.StaticType;
- // 3. Check types
if (seriesType.Kind <> stUnknown) then
begin
if (seriesType.Kind <> TStaticTypeKind.stSeries) then
@@ -670,13 +712,11 @@ begin
raise ETypeException.Create('Lookback parameter for "add" must be an ordinal value.');
end;
- // 4. Create new node
Result := TAst.AddSeriesItem(newSeries.AsIdentifier, newValue, newLookback, TTypes.Void);
end;
function TTypeChecker.VisitNop(const Node: INopNode): IAstNode;
begin
- // This is a leaf node. Assign its final type as Void.
Result := TAst.Nop(TTypes.Void);
end;
@@ -685,19 +725,14 @@ var
seriesType: IStaticType;
newSeries: IAstNode;
begin
- // 1. Visit children
newSeries := Accept(Node.Series);
-
- // 2. Get type
seriesType := newSeries.AsTypedNode.StaticType;
- // 3. Check type
if (seriesType.Kind <> stUnknown)
and (seriesType.Kind <> TStaticTypeKind.stSeries)
and (seriesType.Kind <> TStaticTypeKind.stRecordSeries) then
raise ETypeException.CreateFmt('"length" requires a series, but got %s', [seriesType.ToString]);
- // 4. Create new node
Result := TAst.SeriesLength(newSeries.AsIdentifier, TTypes.Ordinal);
end;
diff --git a/Src/AST/Myc.Ast.Debugger.pas b/Src/AST/Myc.Ast.Debugger.pas
index 8b639b8..9f832f6 100644
--- a/Src/AST/Myc.Ast.Debugger.pas
+++ b/Src/AST/Myc.Ast.Debugger.pas
@@ -25,14 +25,14 @@ type
procedure ShowScope;
protected
- // Overridden to provide the debug-specific visitor factory.
+ // Overridden to provide the debug-specific visitor factory for nested calls (Lambdas).
function CreateVisitorFactory: TEvaluatorFactory; 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;
- // The logging overrides for other Visit... methods
function VisitConstant(const Node: IConstantNode): TDataValue; override;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
function VisitKeyword(const Node: IKeywordNode): TDataValue; override;
@@ -47,6 +47,7 @@ type
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;
end;
implementation
@@ -69,35 +70,21 @@ end;
function TDebugEvaluatorVisitor.CreateVisitorFactory: TEvaluatorFactory;
begin
- // Return a closure that creates a new Debug visitor, using the current context
+ // 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 callingVisitor := Self as IEvaluatorVisitor;
Result :=
function(const AScope: IExecutionScope): IEvaluatorVisitor
begin
- var visitor := callingVisitor as TDebugEvaluatorVisitor;
- Result := TDebugEvaluatorVisitor.Create(AScope, visitor.FLog, visitor.FShowScope, visitor.FIndentLevel);
+ Result := TDebugEvaluatorVisitor.Create(AScope, currentLog, currentShowScope, currentIndent);
end;
end;
-function TDebugEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
-begin
- AppendLine(
- 'FunctionCall ('
- + (if Assigned(Node.StaticTarget) then 'STATIC'
- else 'DYNAMIC')
- + ' PATH) {'
- );
- Indent;
- try
- ShowScope;
- Result := inherited VisitFunctionCall(Node);
- finally
- Unindent;
- end;
- AppendLine(Format('} -> %s', [Result.ToString]));
-end;
-
procedure TDebugEvaluatorVisitor.Indent;
begin
inc(FIndentLevel);
@@ -127,6 +114,7 @@ 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
@@ -136,6 +124,30 @@ begin
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 {');
@@ -192,7 +204,7 @@ end;
function TDebugEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
begin
- AppendLine('IfExpr{');
+ AppendLine('IfExpr {');
Indent;
try
Result := inherited VisitIfExpression(Node);
@@ -240,7 +252,7 @@ end;
function TDebugEvaluatorVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
begin
- AppendLine('TernaryExpr{');
+ AppendLine('TernaryExpr {');
Indent;
try
Result := inherited VisitTernaryExpression(Node);
@@ -252,10 +264,11 @@ end;
function TDebugEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
begin
- AppendLine('Block{');
+ AppendLine('Block {');
Indent;
try
Result := inherited VisitBlockExpression(Node);
+ // Scope might have changed after block execution if vars were defined
ShowScope;
finally
Unindent;
@@ -286,4 +299,10 @@ begin
AppendLine(Format('} -> %s', [Result.ToString]));
end;
+function TDebugEvaluatorVisitor.VisitNop(const Node: INopNode): TDataValue;
+begin
+ AppendLine('Nop (void)');
+ Result := TDataValue.Void;
+end;
+
end.
diff --git a/Src/AST/Myc.Ast.Dumper.pas b/Src/AST/Myc.Ast.Dumper.pas
index 99db514..138125a 100644
--- a/Src/AST/Myc.Ast.Dumper.pas
+++ b/Src/AST/Myc.Ast.Dumper.pas
@@ -15,25 +15,22 @@ uses
type
// Dumps a bound AST into a human-readable format for debugging purposes.
- // It inherits from the abstract TAstVisitor to implement the IAstVisitor interface.
IAstDumper = interface(IAstVisitor)
procedure Execute(const RootNode: IAstNode);
end;
- // Inherits from the new non-generic TAstVisitor base class
TAstDumper = class(TAstVisitor, IAstDumper)
private
FOutput: TStrings;
FIndent: Integer;
procedure Indent;
procedure Unindent;
- // Updated Log methods to include the node for type dumping
procedure Log(const Text: string; const Node: IAstNode = nil); overload;
procedure LogFmt(const Fmt: string; const Args: array of const; const Node: IAstNode = nil); overload;
function FormatAddress(const Addr: TResolvedAddress): string;
protected
- // Override abstract procedures from TAstVisitor
+ // Override abstract PROCEDURES from TAstVisitor (not functions!)
procedure VisitConstant(const Node: IConstantNode); override;
procedure VisitIdentifier(const Node: IIdentifierNode); override;
procedure VisitKeyword(const Node: IKeywordNode); override;
@@ -59,12 +56,8 @@ type
procedure VisitNop(const Node: INopNode); override;
public
- // Creates a new instance of the AST dumper.
constructor Create(const AOutput: TStrings);
-
- // Traverses the given root node and writes the structural information into the output.
class procedure Dump(const RootNode: IAstNode; const Output: TStrings);
-
procedure Execute(const RootNode: IAstNode);
end;
@@ -72,7 +65,7 @@ implementation
uses
Myc.Data.Keyword,
- Myc.Ast.Types; // Added for IStaticType.ToString
+ Myc.Ast.Types;
{ TAstDumper }
@@ -122,14 +115,11 @@ var
staticType: IStaticType;
begin
typeStr := '';
- // Check if the node is typed
if Assigned(Node) and Node.IsTyped then
begin
typedNode := Node.AsTypedNode;
-
staticType := typedNode.StaticType;
if Assigned(staticType) then
- // Append the static type information
typeStr := Format(' ', [staticType.ToString])
else
typeStr := ' ';
@@ -139,7 +129,6 @@ end;
procedure TAstDumper.LogFmt(const Fmt: string; const Args: array of const; const Node: IAstNode = nil);
begin
- // Pass the node to the base Log method
Log(Format(Fmt, Args), Node);
end;
@@ -164,7 +153,6 @@ var
adr: TResolvedAddress;
begin
adr := Node.Address;
-
if adr.Kind <> akUnresolved then
LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(adr)], Node)
else
@@ -208,68 +196,74 @@ end;
procedure TAstDumper.VisitLambdaExpression(const Node: ILambdaExpressionNode);
var
upvalueAddr: TResolvedAddress;
- pair: TPair;
+ symbols: TArray;
+ layout: IScopeLayout;
+ slot: Integer;
+ typ: IStaticType;
begin
- if Assigned(Node.ScopeDescriptor) then
+ LogFmt('LambdaExpression (HasNested: %s)', [Node.HasNestedLambdas.ToString(TUseBoolStrs.True)], Node);
+ Indent;
+
+ // 1. Layout & Symbols
+ if Assigned(Node.Layout) then
begin
- LogFmt('LambdaExpression (HasNested: %s)', [Node.HasNestedLambdas.ToString(TUseBoolStrs.True)], Node);
- Indent;
+ LogFmt('Scope: Layout Slots=%d', [Node.Layout.SlotCount]);
+ if Assigned(Node.Descriptor) then
+ begin
+ Log('Symbol Table:');
+ Indent;
+ layout := Node.Layout;
+ symbols := layout.GetSymbols;
+ TArray.Sort(symbols);
- Log('Parameters:');
- Indent;
- for var param in Node.Parameters do
- param.Accept(Self);
- Unindent;
+ for var name in symbols do
+ begin
+ slot := layout.FindSlot(name);
+ typ := Node.Descriptor.GetSymbolType(slot);
+ LogFmt('"%s" -> Slot %d (Type: %s)', [name, slot, typ.ToString]);
+ end;
+ Unindent;
+ end;
+ end
+ else
+ Log('Scope: No Layout (Raw)');
- LogFmt('Upvalues (%d):', [Length(Node.Upvalues)]);
+ // 3. Parameters
+ Log('Parameters:');
+ Indent;
+ for var param in Node.Parameters do
+ param.Accept(Self);
+ Unindent;
+
+ // 4. Upvalues
+ if Length(Node.Upvalues) > 0 then
+ begin
+ LogFmt('Captured Upvalues (%d):', [Length(Node.Upvalues)]);
Indent;
for upvalueAddr in Node.Upvalues do
Log(FormatAddress(upvalueAddr));
Unindent;
-
- Log('Scope Descriptor:');
- Indent;
- if Assigned(Node.ScopeDescriptor) then
- for pair in Node.ScopeDescriptor.Symbols do
- LogFmt('"%s" -> Slot %d', [pair.Key, pair.Value])
- else
- Log('(none)');
- Unindent;
-
- Log('Body:');
- Node.Body.Accept(Self);
-
- Unindent;
- end
- else
- begin
- Log('LambdaExpression (unbound)', Node);
- Indent;
- Log('Parameters:');
- Indent;
- for var param in Node.Parameters do
- param.Accept(Self);
- Unindent;
- Log('Body:');
- Node.Body.Accept(Self);
- Unindent;
end;
+
+ // 5. Body
+ Log('Body:');
+ Node.Body.Accept(Self);
+
+ Unindent;
end;
procedure TAstDumper.VisitFunctionCall(const Node: IFunctionCallNode);
var
arg: IAstNode;
staticStatus: string;
- sigStr: string; // Added
+ sigStr: string;
argTypes: TArray;
i: Integer;
begin
- sigStr := ''; // Default to empty
+ sigStr := '';
if Assigned(Node.StaticTarget) then
begin
staticStatus := 'Assigned';
-
- // Reconstruct the signature from the available data
SetLength(argTypes, Length(Node.Arguments));
for i := 0 to High(Node.Arguments) do
begin
@@ -278,24 +272,12 @@ begin
else
argTypes[i] := 'Untyped';
end;
-
- // Node.StaticType holds the return type set by the Specializer
sigStr := Format(' ', [string.Join(', ', argTypes), Node.StaticType.ToString]);
end
else
- begin
staticStatus := 'nil';
- end;
- LogFmt(
- 'FunctionCall (IsTailCall: %s, StaticTarget: %s%s)',
- [
- Node.IsTailCall.ToString(TUseBoolStrs.True),
- staticStatus,
- sigStr // Added the signature string
- ],
- Node
- ); // Pass Node to LogFmt to append the
+ LogFmt('FunctionCall (IsTailCall: %s, StaticTarget: %s%s)', [Node.IsTailCall.ToString(TUseBoolStrs.True), staticStatus, sigStr], Node);
Indent;
Log('Callee:');
@@ -338,7 +320,6 @@ procedure TAstDumper.VisitRecurNode(const Node: IRecurNode);
var
arg: IAstNode;
begin
- // 'recur' must be a tail call, which is enforced by the binder.
Log('Recur', Node);
Indent;
LogFmt('Arguments (%d):', [Length(Node.Arguments)]);
@@ -363,7 +344,6 @@ end;
procedure TAstDumper.VisitVariableDeclaration(const Node: IVariableDeclarationNode);
begin
LogFmt('VariableDeclaration (IsBoxed: %s)', [Node.IsBoxed.ToString(TUseBoolStrs.True)], Node);
-
Indent;
Node.Identifier.Accept(Self);
if Assigned(Node.Initializer) then
@@ -386,7 +366,7 @@ end;
procedure TAstDumper.VisitMacroDefinition(const Node: IMacroDefinitionNode);
begin
- Log('MacroDefinition', Node); // Macros are not IAstTypedNode
+ Log('MacroDefinition', Node);
Indent;
Log('Name:');
@@ -410,7 +390,7 @@ end;
procedure TAstDumper.VisitQuasiquote(const Node: IQuasiquoteNode);
begin
- Log('Quasiquote', Node); // Not IAstTypedNode
+ Log('Quasiquote', Node);
Indent;
Node.Expression.Accept(Self);
Unindent;
@@ -418,7 +398,7 @@ end;
procedure TAstDumper.VisitUnquote(const Node: IUnquoteNode);
begin
- Log('Unquote', Node); // Not IAstTypedNode
+ Log('Unquote', Node);
Indent;
Node.Expression.Accept(Self);
Unindent;
@@ -426,7 +406,7 @@ end;
procedure TAstDumper.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode);
begin
- Log('UnquoteSplicing', Node); // Not IAstTypedNode
+ Log('UnquoteSplicing', Node);
Indent;
Node.Expression.Accept(Self);
Unindent;
@@ -491,11 +471,6 @@ begin
Unindent;
end;
-procedure TAstDumper.VisitNop(const Node: INopNode);
-begin
- Log('Nop', Node);
-end;
-
procedure TAstDumper.VisitSeriesLength(const Node: ISeriesLengthNode);
begin
Log('SeriesLength', Node);
@@ -505,4 +480,9 @@ begin
Unindent;
end;
+procedure TAstDumper.VisitNop(const Node: INopNode);
+begin
+ Log('Nop', Node);
+end;
+
end.
diff --git a/Src/AST/Myc.Ast.Environment.pas b/Src/AST/Myc.Ast.Environment.pas
index 6968eb4..15b4448 100644
--- a/Src/AST/Myc.Ast.Environment.pas
+++ b/Src/AST/Myc.Ast.Environment.pas
@@ -22,8 +22,6 @@ type
// --- Monomorphization Cache Definitions ---
- // The key for the specialization cache.
- // (Function Address/ID, [Argument Types])
TMonoCacheKey = record
public
Address: TResolvedAddress;
@@ -31,36 +29,29 @@ type
constructor Create(const AAddress: TResolvedAddress; const AArgTypes: TArray);
end;
- // Comparer for the cache key
TMonoCacheKeyComparer = class(TEqualityComparer)
public
function Equals(const Left, Right: TMonoCacheKey): Boolean; override;
- function GetHashCode(const Value: TMonoCacheKey): Integer; override; // Updated
+ function GetHashCode(const Value: TMonoCacheKey): Integer; override;
end;
- // The cache dictionary itself.
- // Verwendet den generischen TSpecializedMethod-Record aus Myc.Ast.Types
TMonomorphCache = TDictionary;
- // This record holds the executable function and its inferred static type.
TCompiledFunction = record
public
Func: TDataValue.TFunc;
- StaticType: IStaticType; // The full static type of the compiled function (e.g., Method(Ord):Method():Ord)
+ StaticType: IStaticType;
constructor Create(const AFunc: TDataValue.TFunc; const AStaticType: IStaticType);
end;
- // Defines the Strategy for creating an Evaluator.
IExecutionStrategy = interface
function CreateVisitor(const AScope: IExecutionScope): IEvaluatorVisitor;
end;
- // Defines the compile-time macro storage.
IMacroRegistry = interface
{$region 'private'}
function GetParent: IMacroRegistry;
{$endregion}
-
procedure Define(const Node: IMacroDefinitionNode);
function Find(const Name: string): IMacroDefinitionNode;
function CreateChildRegistry: IMacroRegistry;
@@ -72,7 +63,6 @@ type
function Resolve(const Address: TResolvedAddress): IFunctionDefinition;
end;
- // Defines the central environment for compilation and execution.
IEnvironment = interface
{$region 'private'}
function GetRootScope: IExecutionScope;
@@ -84,14 +74,19 @@ type
procedure SetExecutionStrategy(const AStrategy: IExecutionStrategy);
function ExpandMacros(const Node: IAstNode): IAstNode;
- function Bind(const Node: IAstNode; out Descriptor: IScopeDescriptor; const AArgTypes: TArray = []): IAstNode;
- function Specialize(const Node: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode;
+
+ // Updated: Returns Layout, not Descriptor. Performs Binding AND TypeChecking.
+ function Bind(const Node: IAstNode; out Layout: IScopeLayout; const AArgTypes: TArray = []): IAstNode;
+
+ // Updated: Removed Descriptor parameter.
+ function Specialize(const Node: IAstNode): IAstNode;
function Compile(
const ANode: IAstNode;
const Params: TArray = [];
const AArgTypes: TArray = []
): TCompiledFunction; overload;
+
function Compile(const Node: IFunctionDefinition; const ArgTypes: TArray = []): TCompiledFunction; overload;
function CreateEnvironment: IEnvironment;
@@ -122,11 +117,13 @@ type
procedure SetDebugMode(ALog: TStrings; AShowScope: Boolean);
function Run(const ANode: IAstNode; const Params: TArray = []; const Args: TArray = []): TDataValue;
+
function Compile(
const Node: IAstNode;
const Params: TArray = [];
const ArgTypes: TArray = []
- ): TCompiledFunction; overload; experimental;
+ ): TCompiledFunction; overload;
+
function Compile(const Node: IFunctionDefinition; const ArgTypes: TArray = []): TCompiledFunction; overload;
procedure Define(const Name: String; const AScript: IAstNode);
@@ -162,13 +159,12 @@ function TMonoCacheKeyComparer.Equals(const Left, Right: TMonoCacheKey): Boolean
var
i: Integer;
begin
- if not (Left.Address = Right.Address) then // Use operator =
+ if not (Left.Address = Right.Address) then
exit(False);
if Length(Left.ArgTypes) <> Length(Right.ArgTypes) then
exit(False);
- // Compare types
for i := 0 to High(Left.ArgTypes) do
begin
if not Left.ArgTypes[i].IsEqual(Right.ArgTypes[i]) then
@@ -182,26 +178,22 @@ function TMonoCacheKeyComparer.GetHashCode(const Value: TMonoCacheKey): Integer;
var
i: Integer;
hash: Integer;
- typeHash: Integer; // Changed from ptrHash
+ typeHash: Integer;
adr: TResolvedAddress;
begin
adr := Value.Address;
- // 1. Hash the TResolvedAddress components
hash := THashBobJenkins.GetHashValue(adr.Kind, SizeOf(TAddressKind), 0);
hash := THashBobJenkins.GetHashValue(adr.ScopeDepth, SizeOf(Integer), hash);
hash := THashBobJenkins.GetHashValue(adr.SlotIndex, SizeOf(Integer), hash);
- // 2. Iteratively combine the hash of each argument type
for i := 0 to High(Value.ArgTypes) do
begin
- // Get the hash code from the type itself (consistent with IsEqual)
if Assigned(Value.ArgTypes[i]) then
typeHash := Value.ArgTypes[i].GetHashCode
else
typeHash := 0;
- // Combine the new hash (typeHash) with the existing hash (hash)
hash := THashBobJenkins.GetHashValue(typeHash, SizeOf(Integer), hash);
end;
@@ -282,8 +274,8 @@ type
procedure SetExecutionStrategy(const AStrategy: IExecutionStrategy);
function ExpandMacros(const Node: IAstNode): IAstNode;
- function Bind(const Node: IAstNode; out Descriptor: IScopeDescriptor; const ArgTypes: TArray): IAstNode;
- function Specialize(const Node: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode;
+ function Bind(const Node: IAstNode; out Layout: IScopeLayout; const ArgTypes: TArray): IAstNode;
+ function Specialize(const Node: IAstNode): IAstNode;
function Compile(
const Node: IAstNode;
@@ -293,6 +285,8 @@ type
function Compile(const Node: IFunctionDefinition; const ArgTypes: TArray): TCompiledFunction; overload;
end;
+{ TAstEnvironment }
+
constructor TAstEnvironment.Create(const AEnvironment: IEnvironment);
begin
FEnvironment := AEnvironment;
@@ -336,18 +330,10 @@ class function TAstEnvironment.Construct(const Scope: IExecutionScope): TAstEnvi
var
RootScope: IExecutionScope;
begin
- // When constructing the *very first* environment:
- // 1. Create a root scope (parented to nil).
- // 2. Explicitly request library registration (ARegisterLibraries = True).
+ // Initialize root scope with library registration
RootScope := TAst.CreateScope(nil, nil, True);
- Result.Create(
- TEnvironment.Create(
- RootScope, // Use the new root scope
- TMacroRegistryImpl.Create(nil),
- TStandardExecutionStrategy.Create
- )
- );
+ Result.Create(TEnvironment.Create(RootScope, TMacroRegistryImpl.Create(nil), TStandardExecutionStrategy.Create));
end;
function TAstEnvironment.CreateEnvironment: TAstEnvironment;
@@ -358,7 +344,7 @@ end;
procedure TAstEnvironment.Define(const Name: String; const AScript: IAstNode);
begin
var compiled := Compile(AScript);
- RootScope.Define(Name, compiled.Func([]));
+ RootScope.Define(Name, compiled.Func([]), compiled.StaticType);
end;
function TAstEnvironment.GetRootScope: IExecutionScope;
@@ -451,7 +437,6 @@ end;
constructor TFunctionDefinitionRegistry.Create;
begin
inherited Create;
- // We can use the comparer from Myc.Ast.Scope
FMap := TDictionary.Create(TResolvedAddressComparer.Create);
end;
@@ -483,7 +468,6 @@ begin
FRootScope := ARootScope;
FMacroRegistry := AMacroRegistry;
FExecutionStrategy := AExecutionStrategy;
- // Create the isolated, instance-specific cache
FMonomorphCache := TMonomorphCache.Create(TMonoCacheKeyComparer.Create);
FFunctionRegistry := TFunctionDefinitionRegistry.Create;
end;
@@ -499,12 +483,13 @@ begin
Result := FMonomorphCache;
end;
-function TEnvironment.Bind(const Node: IAstNode; out Descriptor: IScopeDescriptor; const ArgTypes: TArray): IAstNode;
+function TEnvironment.Bind(const Node: IAstNode; out Layout: IScopeLayout; const ArgTypes: TArray): IAstNode;
begin
- var boundAst := TAstBinder.Bind(FRootScope.Descriptor, Node, Descriptor, FFunctionRegistry, ArgTypes);
- var typedAst := TTypeChecker.CheckTypes(boundAst, Descriptor);
+ // 1. Bind (produces Layout and Bound AST with Addresses)
+ var boundAst := TAstBinder.Bind(FRootScope.Descriptor.Layout, Node, Layout, FFunctionRegistry, ArgTypes);
- Result := typedAst;
+ // 2. Check Types (produces Typed AST with Descriptors baked into nodes)
+ Result := TTypeChecker.CheckTypes(boundAst, Layout);
end;
function TEnvironment.GetMacroRegistry: IMacroRegistry;
@@ -524,7 +509,6 @@ end;
function TEnvironment.CreateEnvironment: IEnvironment;
begin
- // Create a new child environment. It inherits the parent scope (FRootScope) and does *not* re-register the RTL.
Result := TEnvironment.Create(TAst.CreateScope(FRootScope), TMacroRegistryImpl.Create(FMacroRegistry), FExecutionStrategy);
end;
@@ -534,28 +518,52 @@ function TEnvironment.Compile(
const ArgTypes: TArray
): TCompiledFunction;
var
- desc: IScopeDescriptor;
+ layout: IScopeLayout;
+ descriptor: IScopeDescriptor;
funcType: IStaticType;
finalFunc: TDataValue.TFunc;
+
+ typedNode: IAstNode;
+ specialized: IAstNode;
+ tcoOptimized: IAstNode;
begin
+ // 0. Wrap in Lambda to create an isolatable compilation unit (scope)
var prg := TAst.LambdaExpr(Params, Node);
+ // 1. Expand Macros
var expanded := ExpandMacros(prg);
- var bound := Bind(expanded, desc, ArgTypes);
- Assert(bound.IsTyped);
- funcType := bound.AsTypedNode.StaticType;
+ // 2. Bind & Check Types
+ typedNode := Bind(expanded, layout, ArgTypes);
- var specialized := Specialize(bound, desc);
- var tcoOptimized := TAstTCO.Optimize(specialized);
+ // Retrieve the descriptor from the typed lambda node
+ // (The TypeChecker baked it into the new LambdaExpressionNode)
+ descriptor := typedNode.AsLambdaExpression.Descriptor;
+ Assert(Assigned(descriptor), 'Compiler Logic Error: Descriptor missing after type checking.');
- var visitor := FExecutionStrategy.CreateVisitor(desc.CreateScope(FRootScope));
- var func := tcoOptimized.Accept(visitor).AsMethod;
+ funcType := typedNode.AsTypedNode.StaticType;
+ // 3. Specialize
+ specialized := Specialize(typedNode);
+
+ // 4. Optimize (TCO)
+ tcoOptimized := TAstTCO.Optimize(specialized);
+
+ // 5. Create Evaluator
+ // We create a scope based on the descriptor to run the lambda body.
+ // The lambda itself (as a node) is evaluated to produce a closure.
+ // Since we wrapped the user code in a LambdaExpr, evaluating 'tcoOptimized'
+ // returns the closure (TFunc).
+ var visitor := FExecutionStrategy.CreateVisitor(descriptor.CreateScope(FRootScope));
+
+ // Execute the AST to get the TFunc (Closure)
+ var closure := tcoOptimized.Accept(visitor);
+
+ // Wrap TCO handling for the final result
finalFunc :=
function(const Args: TArray): TDataValue
begin
- Result := func(Args);
+ Result := closure.AsMethod()(Args);
TEvaluatorVisitor.HandleTCO(Result);
end;
@@ -564,25 +572,31 @@ end;
function TEnvironment.Compile(const Node: IFunctionDefinition; const ArgTypes: TArray): TCompiledFunction;
var
- desc: IScopeDescriptor;
+ layout: IScopeLayout;
+ descriptor: IScopeDescriptor;
funcType: IStaticType;
finalFunc: TDataValue.TFunc;
+
+ typedNode, specialized, tcoOptimized: IAstNode;
begin
+ // Same steps as above, but Node is already a definition (Lambda)
var expanded := ExpandMacros(Node);
- var bound := Bind(expanded, desc, ArgTypes);
+ typedNode := Bind(expanded, layout, ArgTypes);
- funcType := bound.AsTypedNode.StaticType;
+ descriptor := typedNode.AsLambdaExpression.Descriptor;
+ Assert(Assigned(descriptor));
+ funcType := typedNode.AsTypedNode.StaticType;
- var specialized := Specialize(bound, desc);
- var tcoOptimized := TAstTCO.Optimize(specialized);
+ specialized := Specialize(typedNode);
+ tcoOptimized := TAstTCO.Optimize(specialized);
- var visitor := FExecutionStrategy.CreateVisitor(desc.CreateScope(FRootScope));
- var func := tcoOptimized.Accept(visitor).AsMethod;
+ var visitor := FExecutionStrategy.CreateVisitor(descriptor.CreateScope(FRootScope));
+ var closure := tcoOptimized.Accept(visitor);
finalFunc :=
function(const Args: TArray): TDataValue
begin
- Result := func(Args);
+ Result := closure.AsMethod()(Args);
TEvaluatorVisitor.HandleTCO(Result);
end;
@@ -606,10 +620,9 @@ begin
Result := FFunctionRegistry;
end;
-function TEnvironment.Specialize(const Node: IAstNode; const ADescriptor: IScopeDescriptor): IAstNode;
+function TEnvironment.Specialize(const Node: IAstNode): IAstNode;
begin
- // Call the new Specializer, passing the environment for cache access
- Result := TStaticSpecializer.Specialize(Self, Node, ADescriptor);
+ Result := TStaticSpecializer.Specialize(Self, Node);
end;
end.
diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas
index 1bb557e..1c793ba 100644
--- a/Src/AST/Myc.Ast.Evaluator.pas
+++ b/Src/AST/Myc.Ast.Evaluator.pas
@@ -18,7 +18,6 @@ type
TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor, IEvaluatorVisitor)
private
FScope: IExecutionScope;
- class var
protected
// IAstVisitor methods made virtual for TDebugEvaluatorVisitor to override
@@ -70,7 +69,7 @@ uses
Myc.Data.Decimal,
Myc.Data.Series,
Myc.Data.Scalar.JSON,
- Myc.Ast.Types; // Added for VisitRecordLiteral
+ Myc.Ast.Types;
// Helper type for TCO via trampolining.
type
@@ -178,27 +177,35 @@ function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNo
var
capturedCells: TArray;
i: Integer;
- sourceAddresses: TArray;
closureScope: IExecutionScope;
visitorFactory: TEvaluatorFactory;
begin
- // Access binder-specific information via the interface
- sourceAddresses := Node.Upvalues;
- SetLength(capturedCells, Length(sourceAddresses));
- for i := 0 to High(sourceAddresses) do
- capturedCells[i] := FScope.Capture(sourceAddresses[i]);
+ // 1. Capture Upvalues
+ // The Node.Upvalues array contains the physical addresses in the *current* scope (FScope)
+ // that map to the closure's upvalues.
+ if Node.Upvalues <> nil then
+ begin
+ SetLength(capturedCells, Length(Node.Upvalues));
+ for i := 0 to High(Node.Upvalues) do
+ capturedCells[i] := FScope.Capture(Node.Upvalues[i]);
+ end
+ else
+ capturedCells := nil;
+ // 2. Determine Parent Scope for Closure
// Memory optimization: a lambda's scope does not need to be kept alive as a parent
// if it contains no nested lambdas that might need to capture from it later.
+ // (Assuming HasNestedLambdas flag is set correctly by Binder)
if Node.HasNestedLambdas then
closureScope := FScope
else
closureScope := nil;
- // Get a factory to create the correct visitor (e.g., debug or production) for the lambda body.
+ // 3. Prepare Visitor Factory
visitorFactory := CreateVisitorFactory();
- var scopeDescriptor := Node.ScopeDescriptor;
+ // 4. Capture Metadata for Closure
+ var descriptor := Node.Descriptor; // Runtime Descriptor (contains types + layout)
var params := Node.Parameters;
var cNode: ILambdaExpressionNode := Node;
@@ -216,9 +223,10 @@ begin
raise EArgumentException.CreateFmt('Argument count mismatch: expected %d, got %d', [Length(params), Length(ArgValues)]);
// Create the new execution scope for this function call.
- lambdaScope := TScope.CreateScope(closureScope, scopeDescriptor, capturedCells);
+ lambdaScope := TScope.CreateScope(closureScope, descriptor, capturedCells);
- // Capture the closure itself in slot 0 for 'recur' to find it.
+ // Capture the closure itself in slot 0 for 'recur' to find it (if needed).
+ // Note: The Binder reserves Slot 0 for .
adr.Kind := akLocalOrParent;
adr.ScopeDepth := 0;
adr.SlotIndex := 0;
@@ -227,8 +235,13 @@ begin
// Populate the scope with the actual parameters passed to the function.
for i := 0 to High(ArgValues) do
begin
- // Parameters in a bound lambda must be bound identifiers.
- adr.SlotIndex := params[i].Address.SlotIndex;
+ // Parameters are bound to specific slots by the Binder.
+ // We access them via the Address stored in the parameter node.
+ adr := params[i].Address;
+
+ // Defensive check: Ensure we are writing to local scope
+ Assert(adr.ScopeDepth = 0);
+
lambdaScope[adr] := ArgValues[i];
end;
@@ -244,8 +257,7 @@ end;
function TEvaluatorVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
begin
// Macro definitions are compile-time constructs.
- // The MacroExpander leaves them in the AST for the Visualizer.
- // The Evaluator must simply ignore them.
+ // The Evaluator ignores them (treated as Void).
Result := TDataValue.Void;
end;
@@ -279,17 +291,14 @@ begin
SetLength(argValues, Length(argNodes));
for i := 0 to High(argNodes) do
begin
- Assert(argNodes[i].IsTyped and (argNodes[i].AsTypedNode.StaticType <> TTypes.Unknown), 'Static call argument is not typed.');
+ // Assuming arguments passed type check
argValues[i] := argNodes[i].Accept(Self);
end;
// 2. Call the static target directly
- // The target is a TDataValue.TFunc expecting TArray
Result := Node.StaticTarget(argValues);
// 3. Handle TCO
- // If the static target itself was a compiled lambda ending in 'recur',
- // it will return a Thunk, which we must handle.
HandleTCO(Result);
end
else
@@ -313,7 +322,6 @@ begin
begin
// This is a non-tail call. It must execute the call and act as the trampoline.
Result := (calleeValue.AsMethod)(argValues);
-
HandleTCO(Result);
end;
end;
@@ -386,7 +394,7 @@ function TEvaluatorVisitor.VisitAssignment(const Node: IAssignmentNode): TDataVa
begin
// Evaluate the new value.
Result := Node.Value.Accept(Self);
- // Assign it. The scope's SetValues implementation now handles boxing correctly.
+ // Assign it.
FScope[Node.Identifier.Address] := Result;
end;
@@ -423,7 +431,7 @@ end;
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
begin
- // The scope's GetValues implementation now handles unboxing automatically.
+ // The scope's GetValues implementation handles unboxing and parent lookup.
Result := FScope[Node.Address];
end;
@@ -493,7 +501,6 @@ begin
vkRecord: Result := TDataValue(baseValue.AsRecord[Node.Member.Value]); // Explicit cast
- // --- NEW GENERIC PATH ---
vkGenericRecord:
begin
var rec := baseValue.AsGenericRecord;
@@ -511,17 +518,14 @@ end;
function TEvaluatorVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
var
i: Integer;
- staticType: IStaticType;
begin
- staticType := Node.StaticType; // Get the type set by TypeChecker
-
- if staticType.Kind = stGenericRecord then
+ // Use the properties populated by the TypeChecker/Binder
+ if Assigned(Node.GenericDefinition) then
begin
- // --- NEW GENERIC PATH ---
+ // --- GENERIC RECORD PATH ---
var genFields: TArray>;
SetLength(genFields, Length(Node.Fields));
- // Evaluate all field values
for i := 0 to High(Node.Fields) do
begin
genFields[i] :=
@@ -531,54 +535,51 @@ begin
);
end;
- // Create the new runtime object and wrap it
var dynRec := TDynamicRecord.Create(genFields);
Result := TDataValue.FromGenericRecord(dynRec);
end
- else if staticType.Kind = stRecord then
+ else if Assigned(Node.ScalarDefinition) then
begin
- // --- EXISTING SCALAR PATH ---
+ // --- SCALAR RECORD PATH ---
var values: TArray;
SetLength(values, Length(Node.Fields));
for i := 0 to High(Node.Fields) do
begin
var valData := Node.Fields[i].Value.Accept(Self);
- // TypeChecker guarantees these are vkScalar
values[i] := valData.AsScalar.Value;
end;
- // Get the definition from the type
- var rec := TScalarRecord.Create(staticType.Definition, values);
+ var rec := TScalarRecord.Create(Node.ScalarDefinition, values);
Result := TDataValue.FromRecord(rec);
end
else
- raise EInvalidOpException.Create('Unknown record literal node type during evaluation.');
+ raise EInvalidOpException.Create('RecordLiteral has no definition (Binder/TypeChecker failure).');
end;
function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
var
address: TResolvedAddress;
begin
- // First, evaluate the initializer to get the variable's initial value.
+ // 1. Evaluate Initializer
if Assigned(Node.Initializer) then
Result := Node.Initializer.Accept(Self)
else
Result := TDataValue.Void;
- // Access properties via interface
+ // 2. Get Address (assigned by Binder)
address := Node.Identifier.Address;
- // Check the IsBoxed flag set by the binder.
+ // 3. Store Value
if Node.IsBoxed then
begin
- // This is a captured variable. Use the clean scope method to create the box.
+ // Capture (heap alloc)
Assert(address.ScopeDepth = 0);
FScope.DefineBoxed(address.SlotIndex, Result);
end
else
begin
- // This is a standard local variable. Store the raw value directly.
+ // Stack alloc
FScope[address] := Result;
end;
end;
@@ -612,8 +613,6 @@ end;
function TEvaluatorVisitor.VisitNop(const Node: INopNode): TDataValue;
begin
- // This node should be rejected by the Binder/TypeChecker.
- // If it reaches the evaluator, it's a compiler bug, but we treat it as Void.
Result := TDataValue.Void;
end;
diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas
index b19c713..fa19c5d 100644
--- a/Src/AST/Myc.Ast.Nodes.pas
+++ b/Src/AST/Myc.Ast.Nodes.pas
@@ -106,7 +106,7 @@ type
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
function VisitRecurNode(const Node: IRecurNode): TDataValue;
- function VisitNop(const Node: INopNode): TDataValue; // Added Nop
+ function VisitNop(const Node: INopNode): TDataValue;
end;
IAstNode = interface(IInterface)
@@ -219,13 +219,23 @@ type
{$region 'private'}
function GetParameters: TArray;
function GetBody: IAstNode;
- function GetScopeDescriptor: IScopeDescriptor;
+
+ // The structural layout. Always present after binding.
+ function GetLayout: IScopeLayout;
+
+ // The runtime descriptor. Can be nil during early compilation phases.
+ function GetDescriptor: IScopeDescriptor;
+
function GetUpvalues: TArray;
function GetHasNestedLambdas: Boolean;
{$endregion}
+
property Parameters: TArray read GetParameters;
property Body: IAstNode read GetBody;
- property ScopeDescriptor: IScopeDescriptor read GetScopeDescriptor;
+
+ property Layout: IScopeLayout read GetLayout;
+ property Descriptor: IScopeDescriptor read GetDescriptor;
+
property Upvalues: TArray read GetUpvalues;
property HasNestedLambdas: Boolean read GetHasNestedLambdas;
end;
diff --git a/Src/AST/Myc.Ast.Scope.pas b/Src/AST/Myc.Ast.Scope.pas
index bd2a2f4..4478549 100644
--- a/Src/AST/Myc.Ast.Scope.pas
+++ b/Src/AST/Myc.Ast.Scope.pas
@@ -13,8 +13,10 @@ uses
type
IExecutionScope = interface;
IScopeDescriptor = interface;
+ IScopeLayout = interface;
+
+ // --- Address & Symbol Types ---
- // Defines how an identifier's address was resolved.
TAddressKind = (akUnresolved, akLocalOrParent, akUpvalue);
TResolvedAddress = record
@@ -23,7 +25,6 @@ type
SlotIndex: Integer;
constructor Create(AKind: TAddressKind; AScopeDepth, ASlotIndex: Integer);
class operator Initialize(out Dest: TResolvedAddress);
- public
class operator Equal(const A: TResolvedAddress; B: TResolvedAddress): Boolean;
end;
@@ -33,7 +34,6 @@ type
function GetHashCode(const Value: TResolvedAddress): Integer; override;
end;
- // A resolved symbol, containing its runtime address and static type.
TResolvedSymbol = record
Address: TResolvedAddress;
StaticType: IStaticType;
@@ -49,48 +49,65 @@ type
property Value: TDataValue read GetValue write SetValue;
end;
- // Describes the layout of a scope: variable names, their slot indices and macros.
+ // --- SCOPE STRUCTURE (Compile Time) ---
+
+ // 1. The Product (Immutable).
+ IScopeLayout = interface
+ {$region 'private'}
+ function GetParent: IScopeLayout;
+ function GetSlotCount: Integer;
+ {$endregion}
+
+ function FindSlot(const Name: string): Integer;
+
+ // Returns all defined symbols in this layout (for debugging/reflection)
+ function GetSymbols: TArray;
+
+ property Parent: IScopeLayout read GetParent;
+ property SlotCount: Integer read GetSlotCount;
+ end;
+
+ // 2. The Builder (Mutable).
+ IScopeBuilder = interface(IScopeLayout)
+ function Define(const Name: string): Integer;
+ function Build: IScopeLayout;
+ end;
+
+ // --- RUNTIME ARTIFACTS ---
+
+ // Describes the semantics of a scope: Layout + Types.
IScopeDescriptor = interface
{$region 'private'}
- function GetParent: IScopeDescriptor;
- function GetSlotCount: Integer;
- function GetSymbols: TDictionary;
- function GetType(SlotIndex: Integer): IStaticType;
+ function GetLayout: IScopeLayout;
+ function GetSymbolType(SlotIndex: Integer): IStaticType;
{$endregion}
+
function CreateScope(const AParent: IExecutionScope): IExecutionScope;
- function Define(const Name: string; const AType: IStaticType = nil): Integer;
- procedure UpdateType(SlotIndex: Integer; const AType: IStaticType);
- function FindSymbol(const Name: string): TResolvedSymbol;
-
- property Parent: IScopeDescriptor read GetParent;
- property SlotCount: Integer read GetSlotCount;
- property Symbols: TDictionary read GetSymbols;
+ property Layout: IScopeLayout read GetLayout;
end;
IExecutionScope = interface
{$region 'private'}
function GetParent: IExecutionScope;
- function GetDescriptor: IScopeDescriptor; // (* ADDED *)
+ function GetDescriptor: IScopeDescriptor;
function GetValues(const Address: TResolvedAddress): TDataValue;
procedure SetValues(const Address: TResolvedAddress; const Value: TDataValue);
{$endregion}
function Define(const Name: string; const Value: TDataValue; const AStaticType: IStaticType = nil): TResolvedAddress;
procedure DefineBoxed(SlotIndex: Integer; const Value: TDataValue);
-
- function Dump: string;
- procedure Clear;
function Capture(const Address: TResolvedAddress): IValueCell;
function GetNameID(const Name: String): Integer;
function Resolve(const Name: string): TResolvedAddress;
- // (* REMOVED: function CreateDescriptor: IScopeDescriptor; *)
+ function Dump: string;
+ procedure Clear;
property Values[const Address: TResolvedAddress]: TDataValue read GetValues write SetValues; default;
property Parent: IExecutionScope read GetParent;
- property Descriptor: IScopeDescriptor read GetDescriptor; // (* ADDED *)
+ property Descriptor: IScopeDescriptor read GetDescriptor;
end;
TScope = record
@@ -99,7 +116,10 @@ type
const Descriptor: IScopeDescriptor;
const CapturedUpvalues: TArray
): IExecutionScope; static;
- class function CreateDescriptor(const Parent: IScopeDescriptor): IScopeDescriptor; static;
+
+ class function CreateBuilder(const ParentLayout: IScopeLayout): IScopeBuilder; static;
+ class function CreateDescriptor(const Layout: IScopeLayout; const Types: TArray): IScopeDescriptor; static;
+ class function CreateRootLayout: IScopeLayout; static;
end;
implementation
@@ -109,26 +129,50 @@ uses
System.SyncObjs;
type
- TScopeDescriptor = class(TInterfacedObject, IScopeDescriptor)
+ // --- Concrete Layout (Immutable) ---
+ TScopeLayout = class(TInterfacedObject, IScopeLayout)
private
- FParent: IScopeDescriptor;
- FSymbols: TDictionary;
- FSlotTypes: TArray;
- function GetParent: IScopeDescriptor;
+ FParent: IScopeLayout;
+ FMap: TDictionary;
+ function GetParent: IScopeLayout;
function GetSlotCount: Integer;
- function GetSymbols: TDictionary;
- function GetType(SlotIndex: Integer): IStaticType;
public
- constructor Create(const AParent: IScopeDescriptor);
+ constructor Create(const AParent: IScopeLayout; AMap: TDictionary);
destructor Destroy; override;
- // (* REMOVED: class function CreateDescriptor(const Scope: IExecutionScope): IScopeDescriptor; *)
- function Define(const Name: string; const AType: IStaticType): Integer;
- procedure UpdateType(SlotIndex: Integer; const AType: IStaticType = nil);
- function FindSymbol(const Name: string): TResolvedSymbol;
- function CreateScope(const Parent: IExecutionScope): IExecutionScope;
- property Symbols: TDictionary read FSymbols;
+ function FindSlot(const Name: string): Integer;
+ function GetSymbols: TArray;
end;
+ // --- Concrete Builder (Mutable) ---
+ TScopeBuilder = class(TInterfacedObject, IScopeBuilder, IScopeLayout)
+ private
+ FParentLayout: IScopeLayout;
+ FMap: TDictionary;
+ function GetParent: IScopeLayout;
+ function GetSlotCount: Integer;
+ public
+ constructor Create(const AParentLayout: IScopeLayout);
+ destructor Destroy; override;
+
+ function Define(const Name: string): Integer;
+ function FindSlot(const Name: string): Integer;
+ function GetSymbols: TArray;
+ function Build: IScopeLayout;
+ end;
+
+ // --- Descriptor Implementation ---
+ TScopeDescriptor = class(TInterfacedObject, IScopeDescriptor)
+ private
+ FLayout: IScopeLayout;
+ FSlotTypes: TArray;
+ function GetLayout: IScopeLayout;
+ function GetSymbolType(SlotIndex: Integer): IStaticType;
+ public
+ constructor Create(const ALayout: IScopeLayout; const ATypes: TArray);
+ function CreateScope(const Parent: IExecutionScope): IExecutionScope;
+ end;
+
+ // --- Execution Scope ---
TExecutionScope = class(TInterfacedObject, IExecutionScope)
private
type
@@ -147,9 +191,35 @@ type
function GetContent: TDataValue; inline;
end;
+ TDynamicLayout = class(TInterfacedObject, IScopeLayout)
+ private
+ [weak]
+ FOwner: TExecutionScope;
+ function GetParent: IScopeLayout;
+ function GetSlotCount: Integer;
+ public
+ constructor Create(AOwner: TExecutionScope);
+ function FindSlot(const Name: string): Integer;
+ function GetSymbols: TArray;
+ end;
+
+ TDynamicDescriptor = class(TInterfacedObject, IScopeDescriptor)
+ private
+ [weak]
+ FOwner: TExecutionScope;
+ FLayout: IScopeLayout;
+ function GetLayout: IScopeLayout;
+ function GetSymbolType(SlotIndex: Integer): IStaticType;
+ public
+ constructor Create(AOwner: TExecutionScope);
+ function CreateScope(const Parent: IExecutionScope): IExecutionScope;
+ end;
+
private
FParent: IExecutionScope;
- FDescriptor: IScopeDescriptor;
+ FStaticDescriptor: IScopeDescriptor;
+ FLayoutIntf: IScopeLayout;
+ FSlotTypes: TArray;
FValues: TArray;
FCapturedUpvalues: TArray;
FNames: TDictionary;
@@ -159,7 +229,7 @@ type
procedure DumpScope(const ABuilder: TStringBuilder; AIndent: Integer);
procedure NeedNameToIndex;
function GetParent: IExecutionScope;
- function GetDescriptor: IScopeDescriptor; // (* ADDED *)
+ function GetDescriptor: IScopeDescriptor;
function GetNameToIndex: TDictionary;
function GetValues(const Address: TResolvedAddress): TDataValue;
procedure SetValues(const Address: TResolvedAddress; const Value: TDataValue);
@@ -173,7 +243,7 @@ type
function Define(const Name: string; const Value: TDataValue; const AStaticType: IStaticType = nil): TResolvedAddress;
procedure DefineBoxed(SlotIndex: Integer; const Value: TDataValue);
function Capture(const Address: TResolvedAddress): IValueCell;
- // (* REMOVED: function CreateDescriptor: IScopeDescriptor; *)
+
property Names: TDictionary read FNames;
property NameStrings: TList read FNameStrings;
property NameToIndex: TDictionary read GetNameToIndex;
@@ -181,7 +251,7 @@ type
property ValuesArray: TArray read FValues;
end;
-{ TResolvedAddressComparer }
+{ TResolvedAddress & Symbol }
function TResolvedAddressComparer.Equals(const Left, Right: TResolvedAddress): Boolean;
begin
@@ -198,8 +268,6 @@ begin
Result := THashBobJenkins.GetHashValue(adr.SlotIndex, SizeOf(Integer), Result);
end;
-{ TResolvedAddress }
-
constructor TResolvedAddress.Create(AKind: TAddressKind; AScopeDepth, ASlotIndex: Integer);
begin
Kind := AKind;
@@ -219,8 +287,6 @@ begin
Dest.SlotIndex := -1;
end;
-{ TResolvedSymbol }
-
constructor TResolvedSymbol.Create(const AAddress: TResolvedAddress; const AStaticType: IStaticType);
begin
Address := AAddress;
@@ -232,7 +298,176 @@ begin
Dest.StaticType := TTypes.Unknown;
end;
-{ TExecutionScope.TValueCell }
+{ TScopeLayout }
+
+constructor TScopeLayout.Create(const AParent: IScopeLayout; AMap: TDictionary);
+begin
+ inherited Create;
+ FParent := AParent;
+ FMap := AMap;
+end;
+
+destructor TScopeLayout.Destroy;
+begin
+ FMap.Free;
+ inherited;
+end;
+
+function TScopeLayout.FindSlot(const Name: string): Integer;
+begin
+ if not FMap.TryGetValue(Name, Result) then
+ Result := -1;
+end;
+
+function TScopeLayout.GetSymbols: TArray;
+begin
+ Result := FMap.Keys.ToArray;
+end;
+
+function TScopeLayout.GetParent: IScopeLayout;
+begin
+ Result := FParent;
+end;
+
+function TScopeLayout.GetSlotCount: Integer;
+begin
+ Result := FMap.Count;
+end;
+
+{ TScopeBuilder }
+
+constructor TScopeBuilder.Create(const AParentLayout: IScopeLayout);
+begin
+ inherited Create;
+ FParentLayout := AParentLayout;
+ FMap := TDictionary.Create;
+end;
+
+destructor TScopeBuilder.Destroy;
+begin
+ FMap.Free;
+ inherited;
+end;
+
+function TScopeBuilder.Define(const Name: string): Integer;
+begin
+ Result := FMap.Count;
+ FMap.Add(Name, Result);
+end;
+
+function TScopeBuilder.FindSlot(const Name: string): Integer;
+begin
+ if not FMap.TryGetValue(Name, Result) then
+ Result := -1;
+end;
+
+function TScopeBuilder.GetSymbols: TArray;
+begin
+ Result := FMap.Keys.ToArray;
+end;
+
+function TScopeBuilder.Build: IScopeLayout;
+begin
+ Result := TScopeLayout.Create(FParentLayout, TDictionary.Create(FMap));
+end;
+
+function TScopeBuilder.GetParent: IScopeLayout;
+begin
+ Result := FParentLayout;
+end;
+
+function TScopeBuilder.GetSlotCount: Integer;
+begin
+ Result := FMap.Count;
+end;
+
+{ TScopeDescriptor }
+
+constructor TScopeDescriptor.Create(const ALayout: IScopeLayout; const ATypes: TArray);
+begin
+ inherited Create;
+ FLayout := ALayout;
+ FSlotTypes := ATypes;
+end;
+
+function TScopeDescriptor.CreateScope(const Parent: IExecutionScope): IExecutionScope;
+begin
+ Result := TExecutionScope.Create(Parent, Self, nil);
+end;
+
+function TScopeDescriptor.GetLayout: IScopeLayout;
+begin
+ Result := FLayout;
+end;
+
+function TScopeDescriptor.GetSymbolType(SlotIndex: Integer): IStaticType;
+begin
+ if (SlotIndex >= 0) and (SlotIndex < Length(FSlotTypes)) then
+ Result := FSlotTypes[SlotIndex]
+ else
+ Result := TTypes.Unknown;
+end;
+
+{ TExecutionScope Proxies }
+
+constructor TExecutionScope.TDynamicLayout.Create(AOwner: TExecutionScope);
+begin
+ inherited Create;
+ FOwner := AOwner;
+end;
+
+function TExecutionScope.TDynamicLayout.FindSlot(const Name: string): Integer;
+begin
+ if not FOwner.Names.TryGetValue(Name, Result) then
+ Result := -1
+ else if not FOwner.NameToIndex.TryGetValue(Result, Result) then
+ Result := -1;
+end;
+
+function TExecutionScope.TDynamicLayout.GetSymbols: TArray;
+begin
+ Result := FOwner.FNames.Keys.ToArray;
+end;
+
+function TExecutionScope.TDynamicLayout.GetParent: IScopeLayout;
+begin
+ if Assigned(FOwner.Parent) then
+ Result := FOwner.Parent.Descriptor.Layout
+ else
+ Result := nil;
+end;
+
+function TExecutionScope.TDynamicLayout.GetSlotCount: Integer;
+begin
+ Result := Length(FOwner.FValues);
+end;
+
+constructor TExecutionScope.TDynamicDescriptor.Create(AOwner: TExecutionScope);
+begin
+ inherited Create;
+ FOwner := AOwner;
+ FLayout := TDynamicLayout.Create(AOwner);
+end;
+
+function TExecutionScope.TDynamicDescriptor.CreateScope(const Parent: IExecutionScope): IExecutionScope;
+begin
+ Result := TExecutionScope.Create(Parent, Self, nil);
+end;
+
+function TExecutionScope.TDynamicDescriptor.GetLayout: IScopeLayout;
+begin
+ Result := FLayout;
+end;
+
+function TExecutionScope.TDynamicDescriptor.GetSymbolType(SlotIndex: Integer): IStaticType;
+begin
+ if (SlotIndex >= 0) and (SlotIndex < Length(FOwner.FSlotTypes)) then
+ Result := FOwner.FSlotTypes[SlotIndex]
+ else
+ Result := TTypes.Unknown;
+end;
+
+{ TExecutionScope }
constructor TExecutionScope.TValueCell.Create(const AValue: TDataValue);
begin
@@ -250,8 +485,6 @@ begin
FValue := AValue;
end;
-{ TExecutionScope }
-
constructor TExecutionScope.Create(
AParent: IExecutionScope;
const ADescriptor: IScopeDescriptor;
@@ -261,18 +494,17 @@ begin
inherited Create;
FParent := AParent;
- // (* MODIFIED: Clean logic for Descriptor initialization *)
if Assigned(ADescriptor) then
- FDescriptor := ADescriptor
- else if Assigned(AParent) then
begin
- // Create a NEW empty descriptor that is a CHILD of the parent's descriptor.
- // Use the Descriptor property of the parent scope.
- FDescriptor := TScopeDescriptor.Create(AParent.Descriptor);
+ FStaticDescriptor := ADescriptor;
+ FLayoutIntf := ADescriptor.Layout;
end
else
- // Create a new root descriptor (parent = nil)
- FDescriptor := TScopeDescriptor.Create(nil);
+ begin
+ FStaticDescriptor := nil;
+ FLayoutIntf := nil;
+ FSlotTypes := [];
+ end;
FValues := [];
FCapturedUpvalues := ACapturedUpvalues;
@@ -288,8 +520,8 @@ begin
FNameStrings := TList.Create;
end;
- if Assigned(ADescriptor) then
- SetLength(FValues, ADescriptor.SlotCount);
+ if Assigned(FLayoutIntf) then
+ SetLength(FValues, FLayoutIntf.SlotCount);
end;
destructor TExecutionScope.Destroy;
@@ -310,7 +542,10 @@ end;
function TExecutionScope.GetDescriptor: IScopeDescriptor;
begin
- Result := FDescriptor;
+ if Assigned(FStaticDescriptor) then
+ Result := FStaticDescriptor
+ else
+ Result := TDynamicDescriptor.Create(Self);
end;
procedure TExecutionScope.Clear;
@@ -327,7 +562,6 @@ begin
case Address.Kind of
akUpvalue:
begin
- Assert(Assigned(FCapturedUpvalues));
Result := FCapturedUpvalues[Address.SlotIndex];
end;
akLocalOrParent:
@@ -356,32 +590,32 @@ begin
end;
end;
-function TExecutionScope.Define(const Name: string; const Value: TDataValue; const AStaticType: IStaticType = nil): TResolvedAddress;
+function TExecutionScope.Define(const Name: string; const Value: TDataValue; const AStaticType: IStaticType): TResolvedAddress;
var
id: Integer;
index: Integer;
- staticType: IStaticType;
begin
NeedNameToIndex;
id := GetNameID(Name);
if FNameToIndex.ContainsKey(id) then
raise Exception.CreateFmt('Variable "%s" is already defined in this scope.', [Name]);
- if Assigned(AStaticType) then
- staticType := AStaticType
- else
- staticType := TTypes.Unknown;
-
index := Length(FValues);
SetLength(FValues, index + 1);
FValues[index].Value := Value;
FValues[index].IsBoxed := False;
+
+ if FStaticDescriptor = nil then
+ begin
+ if index >= Length(FSlotTypes) then
+ SetLength(FSlotTypes, index + 1);
+ if Assigned(AStaticType) then
+ FSlotTypes[index] := AStaticType
+ else
+ FSlotTypes[index] := TTypes.Unknown;
+ end;
+
FNameToIndex.Add(id, index);
-
- // Update the descriptor
- var descSlot := FDescriptor.Define(Name, staticType);
- Assert(descSlot = index, 'Scope descriptor slot mismatch during Define');
-
Result.Kind := akLocalOrParent;
Result.ScopeDepth := 0;
Result.SlotIndex := index;
@@ -389,6 +623,8 @@ end;
procedure TExecutionScope.DefineBoxed(SlotIndex: Integer; const Value: TDataValue);
begin
+ if SlotIndex >= Length(FValues) then
+ SetLength(FValues, SlotIndex + 1);
FValues[SlotIndex].Value := TDataValue.FromIntf(TValueCell.Create(Value));
FValues[SlotIndex].IsBoxed := True;
end;
@@ -414,18 +650,21 @@ begin
for pair in sortedPairs do
begin
- item := FValues[pair.Value];
- if item.IsBoxed then
+ if pair.Value < Length(FValues) then
begin
- boxedStr := ' (Boxed)';
- val := (item.Value.AsIntf).Value;
- end
- else
- begin
- boxedStr := '';
- val := item.Value;
+ item := FValues[pair.Value];
+ if item.IsBoxed then
+ begin
+ boxedStr := ' (Boxed)';
+ val := (item.Value.AsIntf).Value;
+ end
+ else
+ begin
+ boxedStr := '';
+ val := item.Value;
+ end;
+ ABuilder.AppendLine(indentStr + Format(' [%d] %s: %s%s', [pair.Value, FNameStrings[pair.Key], val.ToString, boxedStr]));
end;
- ABuilder.AppendLine(indentStr + Format(' [%d] %s: %s%s', [pair.Value, FNameStrings[pair.Key], val.ToString, boxedStr]));
end;
end
else
@@ -465,7 +704,6 @@ begin
case Address.Kind of
akUpvalue:
begin
- Assert(Assigned(FCapturedUpvalues));
Result := FCapturedUpvalues[Address.SlotIndex].Value;
end;
akLocalOrParent:
@@ -499,9 +737,10 @@ begin
if FNameToIndex = nil then
begin
FNameToIndex := TDictionary.Create;
- if FDescriptor <> nil then
+ if Assigned(FStaticDescriptor) and (FStaticDescriptor.Layout is TScopeLayout) then
begin
- for var item in FDescriptor.Symbols do
+ var map := (FStaticDescriptor.Layout as TScopeLayout).FMap;
+ for var item in map do
FNameToIndex.AddOrSetValue(GetNameId(item.Key), item.Value);
end;
end;
@@ -509,8 +748,7 @@ end;
function TExecutionScope.Resolve(const Name: string): TResolvedAddress;
var
- nameID: Integer;
- slotIndex: Integer;
+ nameID, slotIndex: Integer;
currentScope: IExecutionScope;
depth: Integer;
begin
@@ -542,7 +780,6 @@ begin
case Address.Kind of
akUpvalue:
begin
- Assert(Assigned(FCapturedUpvalues));
FCapturedUpvalues[Address.SlotIndex].Value := Value;
end;
akLocalOrParent:
@@ -569,99 +806,16 @@ begin
Result := Result.AsIntf.Value;
end;
-{ TScopeDescriptor }
-
-constructor TScopeDescriptor.Create(const AParent: IScopeDescriptor);
-begin
- inherited Create;
- FParent := AParent;
- FSymbols := TDictionary.Create;
- FSlotTypes := [];
-end;
-
-destructor TScopeDescriptor.Destroy;
-begin
- FSlotTypes := nil;
- FSymbols.Free;
- inherited;
-end;
-
-function TScopeDescriptor.CreateScope(const Parent: IExecutionScope): IExecutionScope;
-begin
- Result := TExecutionScope.Create(Parent, Self, nil);
-end;
-
-function TScopeDescriptor.Define(const Name: string; const AType: IStaticType): Integer;
-begin
- Result := FSymbols.Count;
- FSymbols.Add(Name, Result);
- SetLength(FSlotTypes, Result + 1);
- FSlotTypes[Result] :=
- if AType <> nil then AType
- else TTypes.Unknown;
-end;
-
-procedure TScopeDescriptor.UpdateType(SlotIndex: Integer; const AType: IStaticType = nil);
-begin
- if (SlotIndex < 0) or (SlotIndex >= Length(FSlotTypes)) then
- raise EArgumentOutOfRangeException.Create('Invalid SlotIndex in UpdateType.');
- FSlotTypes[SlotIndex] :=
- If AType <> nil then AType
- else TTypes.Unknown;
-end;
-
-function TScopeDescriptor.FindSymbol(const Name: string): TResolvedSymbol;
-var
- currentDescriptor: IScopeDescriptor;
- slotIndex: Integer;
-begin
- Result.StaticType := TTypes.Unknown;
- Result.Address.Kind := akUnresolved;
- Result.Address.ScopeDepth := 0;
-
- currentDescriptor := Self;
- while Assigned(currentDescriptor) do
- begin
- if currentDescriptor.Symbols.TryGetValue(Name, slotIndex) then
- begin
- Result.Address.Kind := akLocalOrParent;
- Result.Address.SlotIndex := slotIndex;
- Result.StaticType := currentDescriptor.GetType(slotIndex);
- exit;
- end;
- inc(Result.Address.ScopeDepth);
- currentDescriptor := currentDescriptor.Parent;
- end;
-end;
-
-function TScopeDescriptor.GetParent: IScopeDescriptor;
-begin
- Result := FParent;
-end;
-
-function TScopeDescriptor.GetSlotCount: Integer;
-begin
- Result := FSymbols.Count;
-end;
-
-function TScopeDescriptor.GetSymbols: TDictionary;
-begin
- Result := FSymbols;
-end;
-
-function TScopeDescriptor.GetType(SlotIndex: Integer): IStaticType;
-begin
- if (SlotIndex < 0) or (SlotIndex >= Length(FSlotTypes)) then
- raise EArgumentOutOfRangeException.Create('Invalid SlotIndex in GetType.');
- Result := FSlotTypes[SlotIndex];
-end;
-
{ TScope }
-class function TScope.CreateDescriptor(const Parent: IScopeDescriptor): IScopeDescriptor;
+class function TScope.CreateBuilder(const ParentLayout: IScopeLayout): IScopeBuilder;
begin
- // Factory for a new, empty descriptor
- Result := TScopeDescriptor.Create(Parent);
+ Result := TScopeBuilder.Create(ParentLayout);
+end;
+
+class function TScope.CreateDescriptor(const Layout: IScopeLayout; const Types: TArray): IScopeDescriptor;
+begin
+ Result := TScopeDescriptor.Create(Layout, Types);
end;
class function TScope.CreateScope(
@@ -673,4 +827,9 @@ begin
Result := TExecutionScope.Create(Parent, Descriptor, CapturedUpvalues);
end;
+class function TScope.CreateRootLayout: IScopeLayout;
+begin
+ Result := TScopeLayout.Create(nil, TDictionary.Create);
+end;
+
end.
diff --git a/Src/AST/Myc.Ast.Visitor.pas b/Src/AST/Myc.Ast.Visitor.pas
index 0bba394..2dd7fe5 100644
--- a/Src/AST/Myc.Ast.Visitor.pas
+++ b/Src/AST/Myc.Ast.Visitor.pas
@@ -486,7 +486,7 @@ begin
else
begin
// Use TAst factory and copy properties via interface getters
- Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
+ Result := TAst.LambdaExpr(newParams, newBody, Node.Layout, Node.Descriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
end;
end;
diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas
index 55a5fa8..323fad3 100644
--- a/Src/AST/Myc.Ast.pas
+++ b/Src/AST/Myc.Ast.pas
@@ -56,11 +56,12 @@ type
const AStaticType: IStaticType = nil
): ITernaryExpressionNode; static;
- // Updated Factory Signature
+ // Factory Updated: Includes Layout (mandatory) and Descriptor (optional)
class function LambdaExpr(
const AParameters: TArray;
const ABody: IAstNode;
- const AScopeDescriptor: IScopeDescriptor = nil;
+ const ALayout: IScopeLayout = nil; // Nil allowed only for raw AST construction (before Binder)
+ const ADescriptor: IScopeDescriptor = nil; // Nil allowed before TypeChecker
const AUpvalues: TArray = nil;
const AHasNestedLambdas: Boolean = False;
const AStaticType: IStaticType = nil
@@ -75,7 +76,6 @@ type
class function Unquote(const AExpression: IAstNode): IUnquoteNode; static;
class function UnquoteSplicing(const AExpression: IQuasiquoteNode): IUnquoteSplicingNode; static;
- // Updated Factory Signature
class function FunctionCall(
const ACallee: IAstNode;
const AArguments: TArray;
@@ -91,7 +91,6 @@ type
class function Recur(const AArguments: array of IAstNode; const AStaticType: IStaticType = nil): IRecurNode; static;
class function Block(const AExpressions: array of IAstNode; const AStaticType: IStaticType = nil): IBlockExpressionNode; static;
- // Updated Factory Signature
class function VarDecl(
const AIdentifier: IIdentifierNode;
AInitializer: IAstNode = nil;
@@ -134,9 +133,8 @@ uses
System.Generics.Defaults;
type
- // --- Concrete Class Definitions moved to implementation ---
+ // --- Concrete Class Definitions ---
- // Common base class for AST nodes to reduce boilerplate.
TAstNode = class(TInterfacedObject, IAstNode)
private
function GetKind: TAstNodeKind; virtual; abstract;
@@ -168,7 +166,7 @@ type
function AsAddSeriesItem: IAddSeriesItemNode; virtual;
function AsSeriesLength: ISeriesLengthNode; virtual;
function AsRecur: IRecurNode; virtual;
- function AsNop: INopNode; virtual; // Added Nop
+ function AsNop: INopNode; virtual;
function AsTypedNode: IAstTypedNode; virtual;
@@ -187,16 +185,20 @@ type
property StaticType: IStaticType read GetStaticType;
end;
+ // Updated TLambdaExpressionNode
TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode, IFunctionDefinition)
private
FParameters: TArray;
FBody: IAstNode;
- FScopeDescriptor: IScopeDescriptor;
+ FLayout: IScopeLayout;
+ FDescriptor: IScopeDescriptor;
FUpvalues: TArray;
FHasNestedLambdas: Boolean;
+
function GetParameters: TArray;
function GetBody: IAstNode;
- function GetScopeDescriptor: IScopeDescriptor;
+ function GetLayout: IScopeLayout;
+ function GetDescriptor: IScopeDescriptor;
function GetUpvalues: TArray;
function GetHasNestedLambdas: Boolean;
function GetKind: TAstNodeKind; override;
@@ -205,7 +207,8 @@ type
const AParameters: TArray;
const ABody: IAstNode;
const AStaticType: IStaticType;
- const AScopeDescriptor: IScopeDescriptor;
+ const ALayout: IScopeLayout;
+ const ADescriptor: IScopeDescriptor;
const AUpvalues: TArray;
const AHasNestedLambdas: Boolean
);
@@ -519,8 +522,6 @@ type
property Series: IIdentifierNode read FSeries;
end;
- // TRecordLiteralNode is still needed in the interface
- // for TGenericRecordLiteralNode
TRecordLiteralNode = class(TAstTypedNode, IRecordLiteralNode)
private
FFields: TArray;
@@ -566,7 +567,6 @@ begin
else
Result := TScope.CreateScope(Parent, nil, nil);
- // This should only be True when creating the *root* environment.
if ARegisterLibraries then
begin
for var libProc in FLibraries do
@@ -628,8 +628,6 @@ end;
class function TAst.AssignResult(const AValue: IAstNode): IAssignmentNode;
begin
- // The parser ensures 'Result' is a valid identifier, so we create one.
- // The binder will resolve it.
Result := TAssignmentNode.Create(TAst.Identifier('Result'), AValue, TTypes.Unknown);
end;
@@ -658,7 +656,6 @@ begin
end;
end;
- // Create a new node with the correct type
Result := TConstantNode.Create(AValue, constType);
end;
@@ -758,20 +755,21 @@ end;
class function TAst.LambdaExpr(
const AParameters: TArray;
const ABody: IAstNode;
- const AScopeDescriptor: IScopeDescriptor = nil;
+ const ALayout: IScopeLayout = nil;
+ const ADescriptor: IScopeDescriptor = nil;
const AUpvalues: TArray = nil;
const AHasNestedLambdas: Boolean = False;
const AStaticType: IStaticType = nil
): ILambdaExpressionNode;
begin
- // Updated to call new constructor
Result :=
TLambdaExpressionNode.Create(
AParameters,
ABody,
if AStaticType <> nil then AStaticType
else TTypes.Unknown,
- AScopeDescriptor,
+ ALayout,
+ ADescriptor,
AUpvalues,
AHasNestedLambdas
);
@@ -808,7 +806,6 @@ end;
class function TAst.Nop(const AStaticType: IStaticType = nil): IAstNode;
begin
- // Factory function for the new Nop node
Result := TNopNode.Create(TTypes.Unknown);
end;
@@ -883,7 +880,6 @@ class function TAst.VarDecl(
const AIsBoxed: Boolean = False
): IVariableDeclarationNode;
begin
- // Updated to call new constructor
Result :=
TVariableDeclarationNode.Create(
AIdentifier,
@@ -995,7 +991,6 @@ end;
function TAstNode.AsNop: INopNode;
begin
- // Added Nop implementation
raise ETypeException.Create('Node is not a Nop');
end;
@@ -1249,7 +1244,8 @@ constructor TLambdaExpressionNode.Create(
const AParameters: TArray;
const ABody: IAstNode;
const AStaticType: IStaticType;
- const AScopeDescriptor: IScopeDescriptor;
+ const ALayout: IScopeLayout;
+ const ADescriptor: IScopeDescriptor;
const AUpvalues: TArray;
const AHasNestedLambdas: Boolean
);
@@ -1257,14 +1253,18 @@ begin
inherited Create(AStaticType);
FParameters := AParameters;
FBody := ABody;
- FScopeDescriptor := AScopeDescriptor;
+ FLayout := ALayout;
+ FDescriptor := ADescriptor;
FUpvalues := AUpvalues;
FHasNestedLambdas := AHasNestedLambdas;
+
+ // Consistency Check
+ if Assigned(FDescriptor) and (FDescriptor.Layout <> FLayout) then
+ raise Exception.Create('Consistency Error: Lambda Descriptor does not match Layout.');
end;
destructor TLambdaExpressionNode.Destroy;
begin
- // FScopeDescriptor is an interface, managed by ARC
inherited;
end;
@@ -1288,9 +1288,14 @@ begin
Result := FParameters;
end;
-function TLambdaExpressionNode.GetScopeDescriptor: IScopeDescriptor;
+function TLambdaExpressionNode.GetLayout: IScopeLayout;
begin
- Result := FScopeDescriptor;
+ Result := FLayout;
+end;
+
+function TLambdaExpressionNode.GetDescriptor: IScopeDescriptor;
+begin
+ Result := FDescriptor;
end;
function TLambdaExpressionNode.GetUpvalues: TArray;