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 @@
Form1
- - @@ -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;