From bb0e2fd5afc604bc52cf459a284744c33cfbddf0 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Thu, 28 Aug 2025 00:56:20 +0200 Subject: [PATCH] AST-Playground --- ASTPlayground/ASTPlayground.dpr | 16 + ASTPlayground/ASTPlayground.dproj | 1150 +++++++++++++++++++++ ASTPlayground/ASTPlayground.res | Bin 0 -> 154816 bytes ASTPlayground/MainForm.fmx | 88 ++ ASTPlayground/MainForm.pas | 340 ++++++ IntfExtract/ExtractPascalInterfaces.dproj | 2 +- IntfExtract/ExtractPascalInterfaces.res | Bin 96 -> 96 bytes Src/AST/Myc.Ast.Evaluator.pas | 614 +++++++++++ Src/AST/Myc.Ast.Printer.pas | 191 ++++ Src/AST/Myc.Ast.pas | 478 ++------- Src/Data/Myc.Data.Types.pas | 12 +- dirs.txt | 2 +- 12 files changed, 2500 insertions(+), 393 deletions(-) create mode 100644 ASTPlayground/ASTPlayground.dpr create mode 100644 ASTPlayground/ASTPlayground.dproj create mode 100644 ASTPlayground/ASTPlayground.res create mode 100644 ASTPlayground/MainForm.fmx create mode 100644 ASTPlayground/MainForm.pas create mode 100644 Src/AST/Myc.Ast.Evaluator.pas create mode 100644 Src/AST/Myc.Ast.Printer.pas diff --git a/ASTPlayground/ASTPlayground.dpr b/ASTPlayground/ASTPlayground.dpr new file mode 100644 index 0000000..347f1c1 --- /dev/null +++ b/ASTPlayground/ASTPlayground.dpr @@ -0,0 +1,16 @@ +program ASTPlayground; + +uses + System.StartUpCopy, + FMX.Forms, + MainForm in 'MainForm.pas' {Form1}, + Myc.Ast.Evaluator in '..\Src\AST\Myc.Ast.Evaluator.pas', + Myc.Ast.Printer in '..\Src\AST\Myc.Ast.Printer.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj new file mode 100644 index 0000000..41ba722 --- /dev/null +++ b/ASTPlayground/ASTPlayground.dproj @@ -0,0 +1,1150 @@ + + + {8B68383C-DDF8-4420-855E-98C3AA16D819} + 20.3 + FMX + True + Debug + Win32 + ASTPlayground + 3 + Application + ASTPlayground.dpr + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + ASTPlayground + T:\Myc\Src\Data;T:\Myc\Src\AST;$(DCC_UnitSearchPath) + 1031 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + vclwinx;fmx;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;Skia.Package.RTL;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;bindcompfmx;inetdb;FireDACSqliteDriver;DbxClientDriver;Tee;soapmidas;vclactnband;TeeUI;fmxFireDAC;dbexpress;DBXMySQLDriver;VclSmp;inet;vcltouch;fmxase;dbrtl;Skia.Package.FMX;fmxdae;TeeDB;FireDACMSAccDriver;CustomIPTransport;vcldsnap;DBXInterBaseDriver;IndySystem;Skia.Package.VCL;vcldb;vclFireDAC;bindcomp;FireDACCommon;inetstn;IndyCore;RESTBackendComponents;bindcompdbx;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;adortl;vclimg;FireDACPgDriver;FireDAC;inetdbxpress;xmlrtl;tethering;bindcompvcl;dsnap;CloudService;fmxobj;bindcompvclsmp;FMXTee;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + vclwinx;fmx;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;bindcompfmx;inetdb;FireDACSqliteDriver;DbxClientDriver;Tee;soapmidas;vclactnband;TeeUI;fmxFireDAC;dbexpress;DBXMySQLDriver;VclSmp;inet;vcltouch;fmxase;dbrtl;fmxdae;TeeDB;FireDACMSAccDriver;CustomIPTransport;vcldsnap;DBXInterBaseDriver;IndySystem;Skia.Package.VCL;vcldb;vclFireDAC;bindcomp;FireDACCommon;inetstn;IndyCore;RESTBackendComponents;bindcompdbx;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;adortl;vclimg;FireDACPgDriver;FireDAC;inetdbxpress;xmlrtl;tethering;bindcompvcl;dsnap;CloudService;fmxobj;bindcompvclsmp;FMXTee;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DEBUG;$(DCC_Define) + true + false + true + true + true + true + true + + + false + PerMonitorV2 + true + 1033 + + + PerMonitorV2 + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + PerMonitorV2 + + + PerMonitorV2 + + + + MainSource + + +
Form1
+ fmx +
+ + + + Base + + + Cfg_1 + Base + + + Cfg_2 + Base + +
+ + Delphi.Personality.12 + Application + + + + ASTPlayground.dpr + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + true + + + + + true + + + + + ASTPlayground.exe + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-anydpi-v21 + 1 + + + res\drawable-anydpi-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values-v31 + 1 + + + res\values-v31 + 1 + + + + + res\values-v35 + 1 + + + res\values-v35 + 1 + + + + + res\drawable-anydpi-v26 + 1 + + + res\drawable-anydpi-v26 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-anydpi-v33 + 1 + + + res\drawable-anydpi-v33 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-night-v21 + 1 + + + res\values-night-v21 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable-anydpi-v24 + 1 + + + res\drawable-anydpi-v24 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-night-anydpi-v21 + 1 + + + res\drawable-night-anydpi-v21 + 1 + + + + + res\drawable-anydpi-v31 + 1 + + + res\drawable-anydpi-v31 + 1 + + + + + res\drawable-night-anydpi-v31 + 1 + + + res\drawable-night-anydpi-v31 + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + + + + + + + + + + + + + True + True + + + 12 + + + + +
diff --git a/ASTPlayground/ASTPlayground.res b/ASTPlayground/ASTPlayground.res new file mode 100644 index 0000000000000000000000000000000000000000..29a1295fd51bf34abf41f7919feca0df217d330b GIT binary patch literal 154816 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�KmC6k}d^GnmLKWAqAyR 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 literal 0 HcmV?d00001 diff --git a/ASTPlayground/MainForm.fmx b/ASTPlayground/MainForm.fmx new file mode 100644 index 0000000..c34c2f0 --- /dev/null +++ b/ASTPlayground/MainForm.fmx @@ -0,0 +1,88 @@ +object Form1: TForm1 + Left = 0 + Top = 0 + Caption = 'Form1' + ClientHeight = 638 + ClientWidth = 925 + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + DesignerMasterStyle = 0 + object Panel1: TPanel + Align = Left + Size.Width = 137.000000000000000000 + Size.Height = 638.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + object Test1Button: TButton + Position.X = 24.000000000000000000 + Position.Y = 32.000000000000000000 + TabOrder = 1 + Text = 'Test 1' + TextSettings.Trimming = None + OnClick = Test1ButtonClick + end + object Test2Button: TButton + Position.X = 24.000000000000000000 + Position.Y = 62.000000000000000000 + TabOrder = 2 + Text = 'Test 2' + TextSettings.Trimming = None + OnClick = Test2ButtonClick + end + object PrettyPrintButton: TButton + Position.X = 24.000000000000000000 + Position.Y = 168.000000000000000000 + TabOrder = 3 + Text = 'Print' + TextSettings.Trimming = None + OnClick = PrettyPrintButtonClick + end + object DebugButton: TButton + Position.X = 24.000000000000000000 + Position.Y = 198.000000000000000000 + TabOrder = 4 + Text = 'Debug' + TextSettings.Trimming = None + OnClick = DebugButtonClick + end + object RecursionButton: TButton + Position.X = 24.000000000000000000 + Position.Y = 92.000000000000000000 + Size.Width = 80.000000000000000000 + Size.Height = 22.000000000000000000 + Size.PlatformDefault = False + TabOrder = 5 + Text = 'Recursion' + TextSettings.Trimming = None + OnClick = RecursionButtonClick + end + object ShowScopeBox: TCheckBox + Position.X = 32.000000000000000000 + Position.Y = 224.000000000000000000 + TabOrder = 6 + Text = 'Scope' + end + object FibonacciButton: TButton + Position.X = 24.000000000000000000 + Position.Y = 122.000000000000000000 + TabOrder = 8 + Text = 'Fibonacci' + TextSettings.Trimming = None + OnClick = FibonacciButtonClick + end + end + object Memo1: TMemo + Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] + DataDetectorTypes = [] + StyledSettings = [Size, Style, FontColor] + TextSettings.Font.Family = 'Consolas' + Align = Client + Size.Width = 788.000000000000000000 + Size.Height = 638.000000000000000000 + Size.PlatformDefault = False + TabOrder = 2 + Viewport.Width = 784.000000000000000000 + Viewport.Height = 634.000000000000000000 + end +end diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas new file mode 100644 index 0000000..b0f3b52 --- /dev/null +++ b/ASTPlayground/MainForm.pas @@ -0,0 +1,340 @@ +unit MainForm; + +interface + +uses + System.SysUtils, + System.Types, + System.UITypes, + System.Classes, + System.Variants, + FMX.Types, + FMX.Controls, + FMX.Forms, + FMX.Graphics, + FMX.Dialogs, + FMX.Memo.Types, + FMX.StdCtrls, + FMX.ScrollBox, + FMX.Memo, + FMX.Controls.Presentation, + Myc.Data.Types, + Myc.Ast, + Myc.Ast.Evaluator, + Myc.Ast.Printer; + +type + TForm1 = class(TForm) + Panel1: TPanel; + Memo1: TMemo; + Test1Button: TButton; + Test2Button: TButton; + PrettyPrintButton: TButton; + DebugButton: TButton; + RecursionButton: TButton; + ShowScopeBox: TCheckBox; + FibonacciButton: TButton; + procedure DebugButtonClick(Sender: TObject); + procedure FibonacciButtonClick(Sender: TObject); + procedure PrettyPrintButtonClick(Sender: TObject); + procedure RecursionButtonClick(Sender: TObject); + procedure Test1ButtonClick(Sender: TObject); + procedure Test2ButtonClick(Sender: TObject); + private + // Stores the last AST generated by Test1 or Test2 button clicks. + FLastAst: IAstNode; + public + { Public declarations } + end; + +var + Form1: TForm1; + +implementation + +{$R *.fmx} + +procedure TForm1.DebugButtonClick(Sender: TObject); +var + scope: TExecutionScope; + visitor: IAstVisitor; + result: IDataValue; +begin + if not Assigned(FLastAst) then + begin + Memo1.Lines.Add('No AST has been generated yet.'); + Memo1.Lines.Add('Click "Test 1" or "Test 2" first.'); + exit; + end; + + scope := TExecutionScope.Create(nil); + try + Memo1.Lines.Clear; + Memo1.Lines.Add('--- Debug Evaluator Trace ---'); + + // Create the DEBUG visitor, passing the Memo's Lines as the log output + visitor := TDebugEvaluatorVisitor.Create(scope, Memo1.Lines, ShowScopeBox.IsChecked, 0); + + result := FLastAst.Accept(visitor); + + Memo1.Lines.Add('-----------------------------'); + Memo1.Lines.Add('Final script result: ' + result.AsString); + Memo1.Lines.Add(''); + Memo1.Lines.Add('(AST structure stored. Click "Pretty Print" to view.)'); + + finally + scope.Free; + end; +end; + +procedure TForm1.FibonacciButtonClick(Sender: TObject); +var + scope: TExecutionScope; + visitor: IAstVisitor; + root: IExpressionNode; + result: IDataValue; +begin + // This test defines and calls a deeply recursive fibonacci function. + // var fib = func(n) { + // if (n < 2) then n else fib(n - 1) + fib(n - 2) + // }; + // fib(10); + root := + TAst.Block( + [ + TAst.VarDecl( + TAst.Identifier('fib'), + TAst.LambdaExpr( + [TAst.Identifier('n')], + TAst.IfExpr( + // Condition: n < 2 + TAst.BinaryExpr(TAst.Identifier('n'), boLess, TAst.Constant(TDataType.Ordinal.CreateValue(2))), + // Then branch: n + TAst.Identifier('n'), + // Else branch: fib(n - 1) + fib(n - 2) + TAst.BinaryExpr( + // fib(n - 1) + TAst.FunctionCall( + TAst.Identifier('fib'), + [TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TDataType.Ordinal.CreateValue(1)))] + ), + boAdd, + // fib(n - 2) + TAst.FunctionCall( + TAst.Identifier('fib'), + [TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TDataType.Ordinal.CreateValue(2)))] + ) + ) + ) + ) + ), + // Call the function + TAst.FunctionCall(TAst.Identifier('fib'), [TAst.Constant(TDataType.Ordinal.CreateValue(10))]) + ] + ); + + FLastAst := root; + + scope := TExecutionScope.Create(nil); + try + Memo1.Lines.Clear; + Memo1.Lines.Add('--- Recursive fib(10) ---'); + visitor := TEvaluatorVisitor.Create(scope); + result := root.Accept(visitor); + Memo1.Lines.Add('Result: ' + result.AsString); // Should be 55 + Memo1.Lines.Add(''); + Memo1.Lines.Add('(AST structure stored. Click "Pretty Print" or "Debug" to view.)'); + + finally + scope.Free; + end; +end; + +procedure TForm1.PrettyPrintButtonClick(Sender: TObject); +var + visitor: TPrettyPrintVisitor; +begin + // This button now prints the AST that was stored in FLastAst. + Memo1.Lines.Clear; + Memo1.Lines.Add('--- AST Pretty Print ---'); + + if not Assigned(FLastAst) then + begin + Memo1.Lines.Add('No AST has been generated yet.'); + Memo1.Lines.Add('Click "Test 1" or "Test 2" first.'); + exit; + end; + + visitor := TPrettyPrintVisitor.Create; + try + FLastAst.Accept(visitor); + Memo1.Lines.Add(visitor.GetResult); + finally + // Visitor is an interfaced object and managed automatically. + end; +end; + +procedure TForm1.RecursionButtonClick(Sender: TObject); +var + scope: TExecutionScope; + visitor: IAstVisitor; + root: IExpressionNode; + result: IDataValue; +begin + // This test defines and calls a recursive factorial function. + // var factorial = func(n) { + // if (n < 2) then 1 else n * factorial(n - 1) + // }; + // factorial(5); + root := + TAst.Block( + [ + TAst.VarDecl( + TAst.Identifier('factorial'), + TAst.LambdaExpr( + [TAst.Identifier('n')], + TAst.IfExpr( + // Condition: n < 2 + TAst.BinaryExpr(TAst.Identifier('n'), boLess, TAst.Constant(TDataType.Ordinal.CreateValue(2))), + // Then branch: 1 + TAst.Constant(TDataType.Ordinal.CreateValue(1)), + // Else branch: n * factorial(n - 1) + TAst.BinaryExpr( + TAst.Identifier('n'), + boMultiply, + TAst.FunctionCall( + TAst.Identifier('factorial'), // Recursive call + [TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TDataType.Ordinal.CreateValue(1)))] + ) + ) + ) + ) + ), + // Call the function + TAst.FunctionCall(TAst.Identifier('factorial'), [TAst.Constant(TDataType.Ordinal.CreateValue(6))]) + ] + ); + + FLastAst := root; + + scope := TExecutionScope.Create(nil); + try + Memo1.Lines.Clear; + Memo1.Lines.Add('--- Recursive factorial(6) ---'); + visitor := TEvaluatorVisitor.Create(scope); + result := root.Accept(visitor); + Memo1.Lines.Add('Result: ' + result.AsString); + Memo1.Lines.Add(''); + Memo1.Lines.Add('(AST structure stored. Click "Pretty Print" or "Debug" to view.)'); + + finally + scope.Free; + end; +end; + +procedure TForm1.Test1ButtonClick(Sender: TObject); +var + root: IAstNode; + scope: TExecutionScope; + visitor: IAstVisitor; + result: IDataValue; +begin + // With the new expression-oriented AST, the entire logic can be + // represented as a single block expression. The block itself + // evaluates to the value of its last expression. + root := + TAst.Block( + [ + // var a := 10; (this expression returns void) + TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(TDataType.Ordinal.CreateValue(10))), + // var b := a * 2; (this expression also returns void) + TAst.VarDecl( + TAst.Identifier('b'), + TAst.BinaryExpr(TAst.Identifier('a'), boMultiply, TAst.Constant(TDataType.Ordinal.CreateValue(2))) + ), + // a + b; (this is the last expression, its value becomes the block's value) + TAst.BinaryExpr(TAst.Identifier('a'), boAdd, TAst.Identifier('b')) + ] + ); + + // Store the generated AST for the pretty printer. + FLastAst := root; + + // Evaluate the entire AST with a single call + scope := TExecutionScope.Create(nil); + try + visitor := TEvaluatorVisitor.Create(scope); + result := root.Accept(visitor); + + // Display the result + Memo1.Lines.Clear; + Memo1.Lines.Add('AST execution result:'); + if Assigned(result) then + Memo1.Lines.Add(result.AsString) + else + Memo1.Lines.Add(''); + Memo1.Lines.Add(''); + Memo1.Lines.Add('(AST structure stored. Click "Pretty Print" to view.)'); + finally + scope.Free; + end; +end; + +procedure TForm1.Test2ButtonClick(Sender: TObject); +var + scope: TExecutionScope; + visitor: IAstVisitor; + root: IExpressionNode; + result: IDataValue; +begin + // The entire logic is now encapsulated in a single AST. + // This script defines a factory function, then calls it twice. + root := + TAst.Block( + [ + // 1. Define the factory and assign it to a variable 'createStrategyInstance' + TAst.VarDecl( + TAst.Identifier('createStrategyInstance'), + TAst.LambdaExpr( + [TAst.Identifier('offset')], // The factory parameter + TAst.Block( + [ + // The body of the factory defines the strategy logic + TAst.VarDecl(TAst.Identifier('baseValue'), TAst.Constant(TDataType.Ordinal.CreateValue(100))), + TAst.BinaryExpr(TAst.Identifier('baseValue'), boAdd, TAst.Identifier('offset')) + ] + ) + ) + ), + // 2. Call the factory with the first parameter set. + // The result of this expression (120) is calculated but discarded by the block. + TAst.FunctionCall(TAst.Identifier('createStrategyInstance'), [TAst.Constant(TDataType.Ordinal.CreateValue(20))]), + // 3. Call the factory with the second parameter set. + // As this is the last expression, its result (155) becomes the result of the entire block. + TAst.FunctionCall(TAst.Identifier('createStrategyInstance'), [TAst.Constant(TDataType.Ordinal.CreateValue(55))]) + ] + ); + + // Store the complete AST for the pretty printer. + FLastAst := root; + + // Evaluate the AST. + scope := TExecutionScope.Create(nil); + try + visitor := TEvaluatorVisitor.Create(scope); + Memo1.Lines.Clear; + Memo1.Lines.Add('Factory Pattern Demo (Single AST):'); + + result := root.Accept(visitor); + + Memo1.Lines.Add('The entire script has been executed.'); + Memo1.Lines.Add('Result of the final expression: ' + result.AsString); + Memo1.Lines.Add(''); + Memo1.Lines.Add('(AST structure stored. Click "Pretty Print" to view.)'); + + finally + scope.Free; + end; +end; + +end. diff --git a/IntfExtract/ExtractPascalInterfaces.dproj b/IntfExtract/ExtractPascalInterfaces.dproj index b58b1d0..5e351ae 100644 --- a/IntfExtract/ExtractPascalInterfaces.dproj +++ b/IntfExtract/ExtractPascalInterfaces.dproj @@ -91,7 +91,7 @@ false - -r -o T:\out.txt -dirs T:\Myc\dirs.txt -f "C:\Users\Brummel\Documents\cAlgo\DataExport\ChartDisplay\ChartDisplayMain.pas" + -r -o T:\out.txt -dirs T:\Myc\dirs.txt -f "T:\Myc\ASTPlayground\MainForm.pas" false diff --git a/IntfExtract/ExtractPascalInterfaces.res b/IntfExtract/ExtractPascalInterfaces.res index bf572307691fab4ba172ac48cd5c9239d147af8b..08cdae7a01be19dfee966980299f844b91e97d76 100644 GIT binary patch delta 9 QcmYdDm=MFl%)r0^01Tl4v;Y7A delta 9 QcmYdDm=MFl#K6D+01TZ0vj6}9 diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas new file mode 100644 index 0000000..03c55ec --- /dev/null +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -0,0 +1,614 @@ +// https://g.co/gemini/share/f8965e72f968 + +unit Myc.Ast.Evaluator; + +interface + +uses + System.SysUtils, + System.Classes, // For TStrings + System.Generics.Collections, + Myc.Data.Types, + Myc.Ast; + +type + // Manages the scope of execution, holding variables and their values. + TExecutionScope = class + private + FParent: TExecutionScope; + FVariables: TDictionary; + // Added for recursive dumping + procedure DumpScope(const ABuilder: TStringBuilder; AIndent: Integer); + public + constructor Create(AParent: TExecutionScope = nil); + destructor Destroy; override; + function FindValue(const Name: string; out Value: IDataValue): Boolean; + procedure SetValue(const Name: string; const Value: IDataValue); + // Dumps the content of this scope and all parent scopes to a string. + function Dump: string; + end; + + // A closure is a specific kind of method value that is defined by the evaluator. + // It holds the AST body and its captured scope. + IDataClosureValue = interface(IDataMethodValue) + ['{2704586B-E4BD-47AA-B05D-FD441AE6D818}'] + {$region 'private'} + function GetBody: IExpressionNode; + function GetParameters: TList; + function GetClosureScope: TExecutionScope; + {$endregion} + property Body: IExpressionNode read GetBody; + property Parameters: TList read GetParameters; + property ClosureScope: TExecutionScope read GetClosureScope; + end; + + // TEvaluatorVisitor is the base implementation for evaluating an AST. + TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor) + protected // Changed to protected to be accessible by descendants + FScope: TExecutionScope; + function IsTruthy(const AValue: IDataValue): Boolean; + // Factory method to create a new visitor for a sub-scope (e.g., lambda body) + function CreateVisitorForScope(AScope: TExecutionScope): IAstVisitor; virtual; + public + constructor Create(AScope: TExecutionScope); + function VisitConstant(const Node: IConstantNode): IDataValue; virtual; + function VisitIdentifier(const Node: IIdentifierNode): IDataValue; virtual; + function VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; virtual; + function VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; virtual; + function VisitIfExpression(const Node: IIfExpressionNode): IDataValue; virtual; + function VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; virtual; + function VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; virtual; + function VisitBlockExpression(const Node: IBlockExpressionNode): IDataValue; virtual; + function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IDataValue; virtual; + end; + + // TDebugEvaluatorVisitor now overrides all visit methods for full tracing + TDebugEvaluatorVisitor = class(TEvaluatorVisitor) + private + FLog: TStrings; + FIndentLevel: Integer; + FShowScope: Boolean; + procedure Indent; + procedure Unindent; + procedure AppendLine(const S: string); + procedure ShowScope; + protected + // Override the factory method to create a debug visitor + function CreateVisitorForScope(AScope: TExecutionScope): IAstVisitor; override; + public + constructor Create(AScope: TExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0); + // Override all visit methods + function VisitConstant(const Node: IConstantNode): IDataValue; override; + function VisitIdentifier(const Node: IIdentifierNode): IDataValue; override; + function VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; override; + function VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; override; + function VisitIfExpression(const Node: IIfExpressionNode): IDataValue; override; + function VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; override; + function VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; override; + function VisitBlockExpression(const Node: IBlockExpressionNode): IDataValue; override; + function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IDataValue; override; + end; + +implementation + +type + { TDataClosureValueImpl } + // Concrete implementation of the IDataClosureValue interface. + TDataClosureValueImpl = class(TInterfacedObject, IDataValue, IDataClosureValue) + private + FMethodType: IDataMethodType; + FBody: IExpressionNode; + FParameters: TList; + FClosureScope: TExecutionScope; + // IDataValue + function GetDataType: IDataType; + function GetAsString: string; + // IDataMethodValue + function GetValue: TDataMethodProc; + // IDataClosureValue + function GetBody: IExpressionNode; + function GetParameters: TList; + function GetClosureScope: TExecutionScope; + public + constructor Create( + AMethodType: IDataMethodType; + ABody: IExpressionNode; + AParameters: TList; + ACClosureScope: TExecutionScope + ); + end; + +constructor TDataClosureValueImpl.Create( + AMethodType: IDataMethodType; + ABody: IExpressionNode; + AParameters: TList; + ACClosureScope: TExecutionScope +); +begin + inherited Create; + FMethodType := AMethodType; + FBody := ABody; + FParameters := AParameters; // Note: We are taking ownership of the list reference + FClosureScope := ACClosureScope; +end; + +function TDataClosureValueImpl.GetAsString: string; +begin + Result := ''; +end; + +function TDataClosureValueImpl.GetBody: IExpressionNode; +begin + Result := FBody; +end; + +function TDataClosureValueImpl.GetClosureScope: TExecutionScope; +begin + Result := FClosureScope; +end; + +function TDataClosureValueImpl.GetDataType: IDataType; +begin + Result := FMethodType; +end; + +function TDataClosureValueImpl.GetParameters: TList; +begin + Result := FParameters; +end; + +function TDataClosureValueImpl.GetValue: TDataMethodProc; +begin + // This direct execution path is no longer used for closures. + raise ENotSupportedException.Create('Cannot get raw method proc from a closure object.'); +end; + +{ TExecutionScope } + +constructor TExecutionScope.Create(AParent: TExecutionScope = nil); +begin + inherited Create; + FParent := AParent; + FVariables := TDictionary.Create; +end; + +destructor TExecutionScope.Destroy; +begin + FVariables.Free; + inherited Destroy; +end; + +procedure TExecutionScope.DumpScope(const ABuilder: TStringBuilder; AIndent: Integer); +var + pair: TPair; + indentStr: string; +begin + indentStr := ''.PadLeft(AIndent); + if FVariables.Count > 0 then + begin + for pair in FVariables do + ABuilder.AppendLine(indentStr + Format(' %s: %s', [pair.Key, pair.Value.AsString])); + end + else + begin + ABuilder.AppendLine(indentStr + ' (empty)'); + end; + + if Assigned(FParent) then + begin + ABuilder.AppendLine(indentStr + '[Parent Scope]'); + FParent.DumpScope(ABuilder, AIndent + 2); + end; +end; + +function TExecutionScope.Dump: string; +var + builder: TStringBuilder; +begin + builder := TStringBuilder.Create; + try + builder.AppendLine('[Current Scope]'); + DumpScope(builder, 0); + Result := builder.ToString.TrimRight; + finally + builder.Free; + end; +end; + +function TExecutionScope.FindValue(const Name: string; out Value: IDataValue): Boolean; +begin + Result := FVariables.TryGetValue(Name, Value); + if not Result and Assigned(FParent) then + begin + Result := FParent.FindValue(Name, Value); + end; +end; + +procedure TExecutionScope.SetValue(const Name: string; const Value: IDataValue); +begin + // This defines a variable in the current scope. It can shadow a parent variable. + FVariables.AddOrSetValue(Name, Value); +end; + +{ TEvaluatorVisitor } + +constructor TEvaluatorVisitor.Create(AScope: TExecutionScope); +begin + inherited Create; + Assert(Assigned(AScope)); + FScope := AScope; +end; + +function TEvaluatorVisitor.CreateVisitorForScope(AScope: TExecutionScope): IAstVisitor; +begin + // Base implementation creates a standard evaluator + Result := TEvaluatorVisitor.Create(AScope); +end; + +function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; +var + methodType: IDataMethodType; +begin + // Instead of creating an anonymous method, we now create a data object + // that holds all information required to execute the lambda later. + methodType := TDataType.MethodOf(TDataType.Ordinal, TDataType.Ordinal); // TODO: Infer this + Result := TDataClosureValueImpl.Create(methodType, Node.Body, Node.Parameters, FScope); +end; + +function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; +var + calleeValue: IDataValue; + arguments: TList; + closure: IDataClosureValue; + callScope: TExecutionScope; + innerVisitor: IAstVisitor; +begin + calleeValue := Node.Callee.Accept(Self); + arguments := Node.Arguments; + + if not Supports(calleeValue, IDataClosureValue, closure) then + raise EArgumentException.Create('Expression is not a callable closure.'); + + if (arguments.Count <> 1) then + raise EArgumentException.Create('This simple implementation only supports single-argument calls.'); + + // --- New execution logic is now inside the caller --- + // Create the new scope for the function call, parented by the closure's captured scope. + callScope := TExecutionScope.Create(closure.ClosureScope); + try + // Set argument value + var argValue := arguments[0].Accept(Self); + callScope.SetValue(closure.Parameters[0].Name, argValue); + + // Use the factory method to create the visitor with the correct context (and indent). + innerVisitor := Self.CreateVisitorForScope(callScope); + + // Execute the body with the new visitor. + Result := closure.Body.Accept(innerVisitor); + finally + callScope.Free; + end; +end; + +function TEvaluatorVisitor.IsTruthy(const AValue: IDataValue): Boolean; +begin + // Defines the language's concept of "truthiness". + // For now, only ordinals can be conditions. 0 is false, everything else is true. + if not Assigned(AValue) then + Exit(False); + + case AValue.DataType.Kind of + dkOrdinal: Result := (TDataType.TValue(AValue).AsOrdinal.Value <> 0); + else + Result := False; + end; +end; + +function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): IDataValue; +begin + Result := Node.Value; +end; + +function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): IDataValue; +var + val: IDataValue; +begin + if FScope.FindValue(Node.Name, val) then + Result := val + else + raise EArgumentException.CreateFmt('Identifier not found: "%s"', [Node.Name]); +end; + +function TEvaluatorVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; +var + leftValue, rightValue: IDataValue; + comparisonResult: Boolean; +begin + leftValue := Node.Left.Accept(Self); + rightValue := Node.Right.Accept(Self); + + // Handle comparison operators separately as they can work on different types (for now) + // and always return an Ordinal (boolean). + case Node.Operator of + boEqual, boNotEqual, boLess, boGreater, boLessOrEqual, boGreaterOrEqual: + begin + // Basic comparison for Ordinals + if (leftValue.DataType.Kind = dkOrdinal) and (rightValue.DataType.Kind = dkOrdinal) then + begin + var leftVal := TDataType.TValue(leftValue).AsOrdinal.Value; + var rightVal := TDataType.TValue(rightValue).AsOrdinal.Value; + case Node.Operator of + boEqual: comparisonResult := (leftVal = rightVal); + boNotEqual: comparisonResult := (leftVal <> rightVal); + boLess: comparisonResult := (leftVal < rightVal); + boGreater: comparisonResult := (leftVal > rightVal); + boLessOrEqual: comparisonResult := (leftVal <= rightVal); + boGreaterOrEqual: comparisonResult := (leftVal >= rightVal); + end; + if comparisonResult then + Result := TDataType.Ordinal.CreateValue(1) + else + Result := TDataType.Ordinal.CreateValue(0); + exit; + end + else + raise ENotSupportedException.Create('Comparison is only supported for Ordinal types.'); + end; + end; + + if (leftValue.DataType.Kind <> rightValue.DataType.Kind) then + raise ENotSupportedException.CreateFmt( + 'Binary operations on different types (%s and %s) are not supported', + [leftValue.DataType.Name, rightValue.DataType.Name]); + + case leftValue.DataType.Kind of + dkOrdinal: + begin + var leftOrdinal := TDataType.TValue(leftValue).AsOrdinal; + var rightOrdinal := TDataType.TValue(rightValue).AsOrdinal; + var resultVal: Int64; + + case Node.Operator of + boAdd: resultVal := leftOrdinal.Value + rightOrdinal.Value; + boSubtract: resultVal := leftOrdinal.Value - rightOrdinal.Value; + boMultiply: resultVal := leftOrdinal.Value * rightOrdinal.Value; + boDivide: resultVal := leftOrdinal.Value div rightOrdinal.Value; + else + raise ENotSupportedException.Create('Operator not supported for Ordinal type'); + end; + Result := TDataType.Ordinal.CreateValue(resultVal); + end; + dkText: + begin + if (Node.Operator = boAdd) then + begin + var leftText := TDataType.TValue(leftValue).AsText; + var rightText := TDataType.TValue(rightValue).AsText; + Result := TDataType.Text.CreateValue(leftText.Value + rightText.Value); + end + else + raise ENotSupportedException.Create('Operator not supported for Text type'); + end; + else + raise ENotSupportedException.CreateFmt('Binary operation not supported for type %s', [leftValue.DataType.Name]); + end; +end; + +function TEvaluatorVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; +var + rightValue: IDataValue; + ordinalVal: IDataOrdinalValue; +begin + rightValue := Node.Right.Accept(Self); + + case Node.Operator of + uoNegate: + begin + if (rightValue.DataType.Kind = dkOrdinal) then + begin + ordinalVal := TDataType.TValue(rightValue).AsOrdinal; + Result := TDataType.Ordinal.CreateValue(-ordinalVal.Value); + end + else + raise ENotSupportedException.CreateFmt('Unary "-" not supported for type %s', [rightValue.DataType.Name]); + end; + uoNot: raise ENotImplemented.Create('Unary "not" operator is not yet implemented'); + else + raise ENotSupportedException.Create('Unary operator not supported'); + end; +end; + +function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): IDataValue; +var + conditionValue: IDataValue; +begin + conditionValue := Node.Condition.Accept(Self); + + if IsTruthy(conditionValue) then + Result := Node.ThenBranch.Accept(Self) + else + Result := Node.ElseBranch.Accept(Self); +end; + +function TEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): IDataValue; +var + expression: IExpressionNode; + lastValue: IDataValue; +begin + lastValue := TDataType.Void.Value; + for expression in Node.Expressions do + begin + lastValue := expression.Accept(Self); + end; + Result := lastValue; +end; + +function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IDataValue; +var + varName: string; + initValue: IDataValue; +begin + varName := Node.Identifier.Name; + + // 1. Declare the name in the scope first with a placeholder value (void). + // This makes the name available to the initializer (e.g., a lambda). + FScope.SetValue(varName, TDataType.Void.Value); + + // 2. Evaluate the initializer, which can now recursively reference its own name. + if Assigned(Node.Initializer) then + initValue := Node.Initializer.Accept(Self) + else + initValue := TDataType.Void.Value; + + // 3. Update the variable with the actual initialized value. + FScope.SetValue(varName, initValue); + + Result := TDataType.Void.Value; +end; + +{ TDebugEvaluatorVisitor } + +constructor TDebugEvaluatorVisitor.Create(AScope: TExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0); +begin + inherited Create(AScope); + Assert(Assigned(ALog)); + FLog := ALog; + FIndentLevel := AInitialIndent; + FShowScope := AShowScope; +end; + +function TDebugEvaluatorVisitor.CreateVisitorForScope(AScope: TExecutionScope): IAstVisitor; +begin + // Pass the current indent level to the new child visitor + Result := TDebugEvaluatorVisitor.Create(AScope, FLog, FShowScope, FIndentLevel); +end; + +procedure TDebugEvaluatorVisitor.Indent; +begin + inc(FIndentLevel); +end; + +procedure TDebugEvaluatorVisitor.Unindent; +begin + dec(FIndentLevel); +end; + +procedure TDebugEvaluatorVisitor.AppendLine(const S: string); +begin + var pad := ''; + for var i := 0 to FIndentLevel - 1 do + pad := pad + ':' + ''.PadLeft(3); + FLog.Add(pad + S); +end; + +procedure TDebugEvaluatorVisitor.ShowScope; +begin + if FShowScope then + begin + // Dump the scope content upon entering a block + AppendLine('-- Scope --'); + var scopeDump := FScope.Dump.Split([sLineBreak]); + for var line in scopeDump do + AppendLine(line); + AppendLine('-----------'); + end; +end; + +function TDebugEvaluatorVisitor.VisitConstant(const Node: IConstantNode): IDataValue; +begin + AppendLine(Format('Constant (%s)', [Node.Value.AsString])); + Result := inherited VisitConstant(Node); +end; + +function TDebugEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): IDataValue; +begin + Result := inherited VisitIdentifier(Node); + AppendLine(Format('Identifier "%s" -> %s', [Node.Name, Result.AsString])); +end; + +function TDebugEvaluatorVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; +begin + AppendLine(Format('BinaryExpr "%s" {', [Node.Operator.ToString])); + Indent; + try + Result := inherited VisitBinaryExpression(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.AsString])); +end; + +function TDebugEvaluatorVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; +begin + AppendLine(Format('UnaryExpr "%s" {', [Node.Operator.ToString])); + Indent; + try + Result := inherited VisitUnaryExpression(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.AsString])); +end; + +function TDebugEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): IDataValue; +begin + AppendLine('IfExpr{'); + Indent; + try + Result := inherited VisitIfExpression(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.AsString])); +end; + +function TDebugEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; +begin + AppendLine('LambdaExpr{'); + Indent; + try + Result := inherited VisitLambdaExpression(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.AsString])); +end; + +function TDebugEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; +begin + AppendLine('FunctionCall{'); + Indent; + try + ShowScope; + Result := inherited VisitFunctionCall(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.AsString])); +end; + +function TDebugEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): IDataValue; +begin + AppendLine('Block{'); + Indent; + try + ShowScope; + Result := inherited VisitBlockExpression(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.AsString])); +end; + +function TDebugEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IDataValue; +begin + AppendLine(Format('VarDecl %s :=', [Node.Identifier.Name])); + Indent; + try + Result := inherited VisitVariableDeclaration(Node); + finally + Unindent; + end; +end; + +end. diff --git a/Src/AST/Myc.Ast.Printer.pas b/Src/AST/Myc.Ast.Printer.pas new file mode 100644 index 0000000..045f82f --- /dev/null +++ b/Src/AST/Myc.Ast.Printer.pas @@ -0,0 +1,191 @@ +unit Myc.Ast.Printer; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Collections, + Myc.Data.Types, + Myc.Ast; + +type + TPrettyPrintVisitor = class(TInterfacedObject, IAstVisitor) + private + FBuilder: TStringBuilder; + FIndentLevel: Integer; + procedure Indent; + procedure Unindent; + procedure AppendLine(const S: string); + public + constructor Create; + destructor Destroy; override; + function GetResult: string; + // IAstVisitor + function VisitConstant(const Node: IConstantNode): IDataValue; + function VisitIdentifier(const Node: IIdentifierNode): IDataValue; + function VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; + function VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; + function VisitIfExpression(const Node: IIfExpressionNode): IDataValue; + function VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; + function VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; + function VisitBlockExpression(const Node: IBlockExpressionNode): IDataValue; + function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IDataValue; + end; + +implementation + +{ TPrettyPrintVisitor } + +constructor TPrettyPrintVisitor.Create; +begin + inherited Create; + FBuilder := TStringBuilder.Create; + FIndentLevel := 0; +end; + +destructor TPrettyPrintVisitor.Destroy; +begin + FBuilder.Free; + inherited Destroy; +end; + +function TPrettyPrintVisitor.GetResult: string; +begin + Result := FBuilder.ToString; +end; + +procedure TPrettyPrintVisitor.Indent; +begin + inc(FIndentLevel, 2); +end; + +procedure TPrettyPrintVisitor.Unindent; +begin + dec(FIndentLevel, 2); +end; + +procedure TPrettyPrintVisitor.AppendLine(const S: string); +begin + FBuilder.Append(''.PadLeft(FIndentLevel)); + FBuilder.AppendLine(S); +end; + +function TPrettyPrintVisitor.VisitConstant(const Node: IConstantNode): IDataValue; +begin + AppendLine(Format('Constant (%s)', [Node.Value.AsString])); + Result := TDataType.Void.Value; +end; + +function TPrettyPrintVisitor.VisitIdentifier(const Node: IIdentifierNode): IDataValue; +begin + AppendLine(Format('Identifier (%s)', [Node.Name])); + Result := TDataType.Void.Value; +end; + +function TPrettyPrintVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; +begin + AppendLine(Format('BinaryExpr (%s)', [Node.Operator.ToString])); + Indent; + Node.Left.Accept(Self); + Node.Right.Accept(Self); + Unindent; + Result := TDataType.Void.Value; +end; + +function TPrettyPrintVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; +begin + AppendLine(Format('UnaryExpr (%s)', [Node.Operator.ToString])); + Indent; + Node.Right.Accept(Self); + Unindent; + Result := TDataType.Void.Value; +end; + +function TPrettyPrintVisitor.VisitIfExpression(const Node: IIfExpressionNode): IDataValue; +begin + AppendLine('IfExpr'); + Indent; + AppendLine('Condition:'); + Indent; + Node.Condition.Accept(Self); + Unindent; + AppendLine('Then:'); + Indent; + Node.ThenBranch.Accept(Self); + Unindent; + AppendLine('Else:'); + Indent; + Node.ElseBranch.Accept(Self); + Unindent; + Unindent; + Result := TDataType.Void.Value; +end; + +function TPrettyPrintVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; +var + param: IIdentifierNode; + paramNames: TStringList; +begin + paramNames := TStringList.Create; + try + for param in Node.Parameters do + paramNames.Add(param.Name); + AppendLine(Format('Lambda (params: %s)', [paramNames.CommaText])); + finally + paramNames.Free; + end; + + Indent; + AppendLine('Body:'); + Indent; + Node.Body.Accept(Self); + Unindent; + Unindent; + Result := TDataType.Void.Value; +end; + +function TPrettyPrintVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; +var + arg: IExpressionNode; +begin + AppendLine('FunctionCall'); + Indent; + AppendLine('Callee:'); + Indent; + Node.Callee.Accept(Self); + Unindent; + AppendLine('Arguments:'); + Indent; + for arg in Node.Arguments do + arg.Accept(Self); + Unindent; + Unindent; + Result := TDataType.Void.Value; +end; + +function TPrettyPrintVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): IDataValue; +var + expr: IExpressionNode; +begin + AppendLine('Block'); + Indent; + for expr in Node.Expressions do + expr.Accept(Self); + Unindent; + Result := TDataType.Void.Value; +end; + +function TPrettyPrintVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IDataValue; +begin + AppendLine(Format('VarDecl (%s)', [Node.Identifier.Name])); + if Assigned(Node.Initializer) then + begin + Indent; + Node.Initializer.Accept(Self); + Unindent; + end; + Result := TDataType.Void.Value; +end; + +end. diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index 8274b5d..5b98f80 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -9,14 +9,22 @@ uses type // Operators are now type-safe enums - TBinaryOperator = (boAdd, boSubtract, boMultiply, boDivide); + TBinaryOperator = (boAdd, boSubtract, boMultiply, boDivide, boEqual, boNotEqual, boLess, boGreater, boLessOrEqual, boGreaterOrEqual); TUnaryOperator = (uoNegate, uoNot); + // Helper to convert operators to string + TBinaryOperatorHelper = record helper for TBinaryOperator + function ToString: string; + end; + + TUnaryOperatorHelper = record helper for TUnaryOperator + function ToString: string; + end; + // Forward declarations for interfaces IAstVisitor = interface; IAstNode = interface; IExpressionNode = interface; - IStatementNode = interface; IConstantNode = interface; IIdentifierNode = interface; IBinaryExpressionNode = interface; @@ -24,9 +32,8 @@ type IIfExpressionNode = interface; ILambdaExpressionNode = interface; IFunctionCallNode = interface; - IBlockStatementNode = interface; - IVariableDeclarationStatementNode = interface; - IExpressionStatementNode = interface; + IBlockExpressionNode = interface; + IVariableDeclarationNode = interface; // --- Abstract Node Interfaces --- @@ -37,13 +44,10 @@ type end; // Abstract interface for all nodes that evaluate to a value. + // In this paradigm, all nodes are expressions. IExpressionNode = interface(IAstNode) end; - // Abstract interface for all nodes that perform an action. - IStatementNode = interface(IAstNode) - end; - // --- Concrete Expression Node Interfaces --- IConstantNode = interface(IExpressionNode) @@ -94,10 +98,10 @@ type ILambdaExpressionNode = interface(IExpressionNode) {$region 'private'} function GetParameters: TList; - function GetBody: IAstNode; // Can be an expression or a block statement + function GetBody: IExpressionNode; // Body is now always an expression {$endregion} property Parameters: TList read GetParameters; - property Body: IAstNode read GetBody; + property Body: IExpressionNode read GetBody; end; IFunctionCallNode = interface(IExpressionNode) @@ -109,16 +113,16 @@ type property Arguments: TList read GetArguments; end; - // --- Concrete Statement Node Interfaces --- - - IBlockStatementNode = interface(IStatementNode) + // A block is an expression that returns the value of its last expression. + IBlockExpressionNode = interface(IExpressionNode) {$region 'private'} - function GetStatements: TList; + function GetExpressions: TList; {$endregion} - property Statements: TList read GetStatements; + property Expressions: TList read GetExpressions; end; - IVariableDeclarationStatementNode = interface(IStatementNode) + // A variable declaration is an expression that returns a void value. + IVariableDeclarationNode = interface(IExpressionNode) {$region 'private'} function GetIdentifier: IIdentifierNode; function GetInitializer: IExpressionNode; // Can be nil @@ -127,17 +131,9 @@ type property Initializer: IExpressionNode read GetInitializer; end; - IExpressionStatementNode = interface(IStatementNode) - {$region 'private'} - function GetExpression: IExpressionNode; - {$endregion} - property Expression: IExpressionNode read GetExpression; - end; - // All visitor methods are functions returning a value. IAstVisitor = interface ['{5F4110E9-0158-41E9-A512-E57A843E8A5A}'] - // Expression visitors function VisitConstant(const Node: IConstantNode): IDataValue; function VisitIdentifier(const Node: IIdentifierNode): IDataValue; function VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; @@ -145,16 +141,12 @@ type function VisitIfExpression(const Node: IIfExpressionNode): IDataValue; function VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; function VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; - - // Statement visitors - function VisitBlockStatement(const Node: IBlockStatementNode): IDataValue; - function VisitVariableDeclarationStatement(const Node: IVariableDeclarationStatementNode): IDataValue; - function VisitExpressionStatement(const Node: IExpressionStatementNode): IDataValue; + function VisitBlockExpression(const Node: IBlockExpressionNode): IDataValue; + function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IDataValue; end; // Record acting as a namespace for the factory functions. TAst = record - // Expressions class function Constant(AValue: IDataValue): IConstantNode; static; class function Identifier(AName: string): IIdentifierNode; static; class function BinaryExpr( @@ -167,53 +159,46 @@ type const ACondition: IExpressionNode; const AThenBranch, AElseBranch: IExpressionNode ): IIfExpressionNode; static; - class function LambdaExpr(const AParameters: array of IIdentifierNode; const ABody: IAstNode): ILambdaExpressionNode; static; + class function LambdaExpr(const AParameters: array of IIdentifierNode; const ABody: IExpressionNode): ILambdaExpressionNode; static; class function FunctionCall(const ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode; static; - // Statements - class function Block(const AStatements: array of IStatementNode): IBlockStatementNode; static; - class function VarDecl( - const AIdentifier: IIdentifierNode; - AInitializer: IExpressionNode - ): IVariableDeclarationStatementNode; static; - class function ExprStmt(const AExpression: IExpressionNode): IExpressionStatementNode; static; - end; - - // Manages the scope of execution, holding variables and their values. - TExecutionScope = class - private - FParent: TExecutionScope; - FVariables: TDictionary; - public - constructor Create(AParent: TExecutionScope = nil); - destructor Destroy; override; - function FindValue(const Name: string; out Value: IDataValue): Boolean; - procedure SetValue(const Name: string; const Value: IDataValue); - end; - - // TEvaluatorVisitor is stateless and thread-safe. - TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor) - private - FScope: TExecutionScope; - function IsTruthy(const AValue: IDataValue): Boolean; - public - constructor Create(AScope: TExecutionScope); - // Expression visitors - function VisitConstant(const Node: IConstantNode): IDataValue; - function VisitIdentifier(const Node: IIdentifierNode): IDataValue; - function VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; - function VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; - function VisitIfExpression(const Node: IIfExpressionNode): IDataValue; - function VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; - function VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; - - // Statement visitors - function VisitBlockStatement(const Node: IBlockStatementNode): IDataValue; - function VisitVariableDeclarationStatement(const Node: IVariableDeclarationStatementNode): IDataValue; - function VisitExpressionStatement(const Node: IExpressionStatementNode): IDataValue; + class function Block(const AExpressions: array of IExpressionNode): IBlockExpressionNode; static; + class function VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationNode; static; end; implementation +{ TBinaryOperatorHelper } + +function TBinaryOperatorHelper.ToString: string; +begin + case Self of + boAdd: Result := '+'; + boSubtract: Result := '-'; + boMultiply: Result := '*'; + boDivide: Result := '/'; + boEqual: Result := '=='; + boNotEqual: Result := '!='; + boLess: Result := '<'; + boGreater: Result := '>'; + boLessOrEqual: Result := '<='; + boGreaterOrEqual: Result := '>='; + else + Result := '?'; + end; +end; + +{ TUnaryOperatorHelper } + +function TUnaryOperatorHelper.ToString: string; +begin + case Self of + uoNegate: Result := '-'; + uoNot: Result := 'not'; + else + Result := '?'; + end; +end; + type TConstantNodeImpl = class(TInterfacedObject, IConstantNode) private @@ -273,11 +258,11 @@ type TLambdaExpressionNodeImpl = class(TInterfacedObject, ILambdaExpressionNode) private FParameters: TList; - FBody: IAstNode; + FBody: IExpressionNode; function GetParameters: TList; - function GetBody: IAstNode; + function GetBody: IExpressionNode; public - constructor Create(AParameters: TList; ABody: IAstNode); + constructor Create(AParameters: TList; ABody: IExpressionNode); destructor Destroy; override; function Accept(const Visitor: IAstVisitor): IDataValue; end; @@ -294,17 +279,17 @@ type function Accept(const Visitor: IAstVisitor): IDataValue; end; - TBlockStatementNodeImpl = class(TInterfacedObject, IBlockStatementNode) + TBlockExpressionNodeImpl = class(TInterfacedObject, IBlockExpressionNode) private - FStatements: TList; - function GetStatements: TList; + FExpressions: TList; + function GetExpressions: TList; public - constructor Create(AStatements: TList); + constructor Create(AExpressions: TList); destructor Destroy; override; function Accept(const Visitor: IAstVisitor): IDataValue; end; - TVariableDeclarationStatementNodeImpl = class(TInterfacedObject, IVariableDeclarationStatementNode) + TVariableDeclarationNodeImpl = class(TInterfacedObject, IVariableDeclarationNode) private FIdentifier: IIdentifierNode; FInitializer: IExpressionNode; @@ -315,15 +300,6 @@ type function Accept(const Visitor: IAstVisitor): IDataValue; end; - TExpressionStatementNodeImpl = class(TInterfacedObject, IExpressionStatementNode) - private - FExpression: IExpressionNode; - function GetExpression: IExpressionNode; - public - constructor Create(AExpression: IExpressionNode); - function Accept(const Visitor: IAstVisitor): IDataValue; - end; - { TAst - Factory Function Implementations } class function TAst.Constant(AValue: IDataValue): IConstantNode; @@ -351,7 +327,7 @@ begin Result := TIfExpressionNodeImpl.Create(ACondition, AThenBranch, AElseBranch); end; -class function TAst.LambdaExpr(const AParameters: array of IIdentifierNode; const ABody: IAstNode): ILambdaExpressionNode; +class function TAst.LambdaExpr(const AParameters: array of IIdentifierNode; const ABody: IExpressionNode): ILambdaExpressionNode; var paramList: TList; param: IIdentifierNode; @@ -373,25 +349,20 @@ begin Result := TFunctionCallNodeImpl.Create(ACallee, argList); end; -class function TAst.Block(const AStatements: array of IStatementNode): IBlockStatementNode; +class function TAst.Block(const AExpressions: array of IExpressionNode): IBlockExpressionNode; var - stmtList: TList; - stmt: IStatementNode; + exprList: TList; + expr: IExpressionNode; begin - stmtList := TList.Create; - for stmt in AStatements do - stmtList.Add(stmt); - Result := TBlockStatementNodeImpl.Create(stmtList); + exprList := TList.Create; + for expr in AExpressions do + exprList.Add(expr); + Result := TBlockExpressionNodeImpl.Create(exprList); end; -class function TAst.VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationStatementNode; +class function TAst.VarDecl(const AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationNode; begin - Result := TVariableDeclarationStatementNodeImpl.Create(AIdentifier, AInitializer); -end; - -class function TAst.ExprStmt(const AExpression: IExpressionNode): IExpressionStatementNode; -begin - Result := TExpressionStatementNodeImpl.Create(AExpression); + Result := TVariableDeclarationNodeImpl.Create(AIdentifier, AInitializer); end; { TConstantNodeImpl } @@ -516,7 +487,7 @@ end; { TLambdaExpressionNodeImpl } -constructor TLambdaExpressionNodeImpl.Create(AParameters: TList; ABody: IAstNode); +constructor TLambdaExpressionNodeImpl.Create(AParameters: TList; ABody: IExpressionNode); begin inherited Create; FParameters := AParameters; @@ -534,7 +505,7 @@ begin Result := Visitor.VisitLambdaExpression(Self); end; -function TLambdaExpressionNodeImpl.GetBody: IAstNode; +function TLambdaExpressionNodeImpl.GetBody: IExpressionNode; begin Result := FBody; end; @@ -574,315 +545,52 @@ begin Result := FCallee; end; -{ TBlockStatementNodeImpl } +{ TBlockExpressionNodeImpl } -constructor TBlockStatementNodeImpl.Create(AStatements: TList); +constructor TBlockExpressionNodeImpl.Create(AExpressions: TList); begin inherited Create; - FStatements := AStatements; + FExpressions := AExpressions; end; -destructor TBlockStatementNodeImpl.Destroy; +destructor TBlockExpressionNodeImpl.Destroy; begin - FStatements.Free; + FExpressions.Free; inherited Destroy; end; -function TBlockStatementNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; +function TBlockExpressionNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin - Result := Visitor.VisitBlockStatement(Self); + Result := Visitor.VisitBlockExpression(Self); end; -function TBlockStatementNodeImpl.GetStatements: TList; +function TBlockExpressionNodeImpl.GetExpressions: TList; begin - Result := FStatements; + Result := FExpressions; end; -{ TVariableDeclarationStatementNodeImpl } +{ TVariableDeclarationNodeImpl } -constructor TVariableDeclarationStatementNodeImpl.Create(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode); +constructor TVariableDeclarationNodeImpl.Create(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode); begin inherited Create; FIdentifier := AIdentifier; FInitializer := AInitializer; end; -function TVariableDeclarationStatementNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; +function TVariableDeclarationNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin - Result := Visitor.VisitVariableDeclarationStatement(Self); + Result := Visitor.VisitVariableDeclaration(Self); end; -function TVariableDeclarationStatementNodeImpl.GetIdentifier: IIdentifierNode; +function TVariableDeclarationNodeImpl.GetIdentifier: IIdentifierNode; begin Result := FIdentifier; end; -function TVariableDeclarationStatementNodeImpl.GetInitializer: IExpressionNode; +function TVariableDeclarationNodeImpl.GetInitializer: IExpressionNode; begin Result := FInitializer; end; -{ TExpressionStatementNodeImpl } - -constructor TExpressionStatementNodeImpl.Create(AExpression: IExpressionNode); -begin - inherited Create; - FExpression := AExpression; -end; - -function TExpressionStatementNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; -begin - Result := Visitor.VisitExpressionStatement(Self); -end; - -function TExpressionStatementNodeImpl.GetExpression: IExpressionNode; -begin - Result := FExpression; -end; - -{ TExecutionScope } - -constructor TExecutionScope.Create(AParent: TExecutionScope = nil); -begin - inherited Create; - FParent := AParent; - FVariables := TDictionary.Create; -end; - -destructor TExecutionScope.Destroy; -begin - FVariables.Free; - inherited Destroy; -end; - -function TExecutionScope.FindValue(const Name: string; out Value: IDataValue): Boolean; -begin - Result := FVariables.TryGetValue(Name, Value); - if not Result and Assigned(FParent) then - begin - Result := FParent.FindValue(Name, Value); - end; -end; - -procedure TExecutionScope.SetValue(const Name: string; const Value: IDataValue); -begin - // This defines a variable in the current scope. It can shadow a parent variable. - FVariables.AddOrSetValue(Name, Value); -end; - -{ TEvaluatorVisitor } - -constructor TEvaluatorVisitor.Create(AScope: TExecutionScope); -begin - inherited Create; - Assert(Assigned(AScope)); - FScope := AScope; -end; - -function TEvaluatorVisitor.IsTruthy(const AValue: IDataValue): Boolean; -begin - // Defines the language's concept of "truthiness". - // For now, only ordinals can be conditions. 0 is false, everything else is true. - if not Assigned(AValue) then - Exit(False); - - case AValue.DataType.Kind of - dkOrdinal: Result := (TDataType.TValue(AValue).AsOrdinal.Value <> 0); - else - Result := False; - end; -end; - -function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): IDataValue; -begin - Result := Node.Value; -end; - -function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): IDataValue; -var - val: IDataValue; -begin - if FScope.FindValue(Node.Name, val) then - Result := val - else - raise EArgumentException.CreateFmt('Identifier not found: "%s"', [Node.Name]); -end; - -function TEvaluatorVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; -var - leftValue, rightValue: IDataValue; -begin - leftValue := Node.Left.Accept(Self); - rightValue := Node.Right.Accept(Self); - - if (leftValue.DataType.Kind <> rightValue.DataType.Kind) then - raise ENotSupportedException.CreateFmt( - 'Binary operations on different types (%s and %s) are not supported', - [leftValue.DataType.Name, rightValue.DataType.Name]); - - case leftValue.DataType.Kind of - dkOrdinal: - begin - var leftOrdinal := TDataType.TValue(leftValue).AsOrdinal; - var rightOrdinal := TDataType.TValue(rightValue).AsOrdinal; - var resultVal: Int64; - - case Node.Operator of - boAdd: resultVal := leftOrdinal.Value + rightOrdinal.Value; - boSubtract: resultVal := leftOrdinal.Value - rightOrdinal.Value; - boMultiply: resultVal := leftOrdinal.Value * rightOrdinal.Value; - boDivide: resultVal := leftOrdinal.Value div rightOrdinal.Value; - else - raise ENotSupportedException.Create('Operator not supported for Ordinal type'); - end; - Result := TDataType.Ordinal.CreateValue(resultVal); - end; - dkText: - begin - if (Node.Operator = boAdd) then - begin - var leftText := TDataType.TValue(leftValue).AsText; - var rightText := TDataType.TValue(rightValue).AsText; - Result := TDataType.Text.CreateValue(leftText.Value + rightText.Value); - end - else - raise ENotSupportedException.Create('Operator not supported for Text type'); - end; - else - raise ENotSupportedException.CreateFmt('Binary operation not supported for type %s', [leftValue.DataType.Name]); - end; -end; - -function TEvaluatorVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; -var - rightValue: IDataValue; - ordinalVal: IDataOrdinalValue; -begin - rightValue := Node.Right.Accept(Self); - - case Node.Operator of - uoNegate: - begin - if (rightValue.DataType.Kind = dkOrdinal) then - begin - ordinalVal := TDataType.TValue(rightValue).AsOrdinal; - Result := TDataType.Ordinal.CreateValue(-ordinalVal.Value); - end - else - raise ENotSupportedException.CreateFmt('Unary "-" not supported for type %s', [rightValue.DataType.Name]); - end; - uoNot: raise ENotImplemented.Create('Unary "not" operator is not yet implemented'); - else - raise ENotSupportedException.Create('Unary operator not supported'); - end; -end; - -function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): IDataValue; -var - conditionValue: IDataValue; -begin - conditionValue := Node.Condition.Accept(Self); - - if IsTruthy(conditionValue) then - Result := Node.ThenBranch.Accept(Self) - else - Result := Node.ElseBranch.Accept(Self); -end; - -function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; -var - lambdaBody: IAstNode; - lambdaParams: TList; - closureScope: TExecutionScope; - methodType: IDataMethodType; -begin - closureScope := FScope; - lambdaBody := Node.Body; - lambdaParams := Node.Parameters; - methodType := TDataType.MethodOf(TDataType.Ordinal, TDataType.Ordinal); - - Result := - methodType.CreateValue( - function(const AValue: IDataValue): IDataValue - var - callScope: TExecutionScope; - innerVisitor: IAstVisitor; - begin - callScope := TExecutionScope.Create(closureScope); - try - if (lambdaParams.Count <> 1) then - raise EArgumentException.Create('This simple implementation only supports single-parameter lambdas.'); - - callScope.SetValue(lambdaParams[0].Name, AValue); - - innerVisitor := TEvaluatorVisitor.Create(callScope); - Result := lambdaBody.Accept(innerVisitor); - finally - callScope.Free; - end; - end - ); -end; - -function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; -var - calleeValue, argValue: IDataValue; - methodProc: TDataMethodProc; - arguments: TList; -begin - calleeValue := Node.Callee.Accept(Self); - - if (calleeValue.DataType.Kind <> dkMethod) then - raise EArgumentException.Create('Expression is not callable.'); - - arguments := Node.Arguments; - if (arguments.Count <> 1) then - raise EArgumentException.Create('This simple implementation only supports single-argument calls.'); - - argValue := arguments[0].Accept(Self); - - methodProc := TDataType.TValue(calleeValue).AsMethod.Value; - Result := methodProc(argValue); -end; - -// --- Statement Visitor Implementations --- - -function TEvaluatorVisitor.VisitBlockStatement(const Node: IBlockStatementNode): IDataValue; -var - statement: IStatementNode; -begin - // Execute all statements in the block sequentially. - for statement in Node.Statements do - begin - statement.Accept(Self); // The result is ignored. - end; - Result := TDataType.Void.Value; -end; - -function TEvaluatorVisitor.VisitVariableDeclarationStatement(const Node: IVariableDeclarationStatementNode): IDataValue; -var - varName: string; - initValue: IDataValue; -begin - varName := Node.Identifier.Name; - - // Evaluate the initializer expression, if it exists. - if Assigned(Node.Initializer) then - initValue := Node.Initializer.Accept(Self) - else - initValue := TDataType.Void.Value; // Default value if no initializer is provided. - - // Define the variable in the current scope. - FScope.SetValue(varName, initValue); - - Result := TDataType.Void.Value; -end; - -function TEvaluatorVisitor.VisitExpressionStatement(const Node: IExpressionStatementNode): IDataValue; -begin - // Evaluate the expression for its side-effects and discard the result. - Node.Expression.Accept(Self); - Result := TDataType.Void.Value; -end; - end. diff --git a/Src/Data/Myc.Data.Types.pas b/Src/Data/Myc.Data.Types.pas index c67706d..42567e6 100644 --- a/Src/Data/Myc.Data.Types.pas +++ b/Src/Data/Myc.Data.Types.pas @@ -553,7 +553,7 @@ type class operator Implicit(const A: TDataType.TMethod): IDataMethodType; overload; inline; property ArgType: IDataType read GetArgType; property ResultType: IDataType read GetResultType; - function CreateValue(const Proc: TProc): TDataType.TMethod.TValue; inline; + function CreateValue(const Proc: TProc): TDataType.TMethod.TValue; end; // Helper to cast a generic TValue to a specific value helper. @@ -598,11 +598,11 @@ type class operator Implicit(const A: TDataType.TVector): TDataType; overload; inline; // Type factories - class function Void: TDataType.TVoid; static; inline; - class function Ordinal: TDataType.TOrdinal; static; inline; - class function Float: TDataType.TFloat; static; inline; - class function Text: TDataType.TText; static; inline; - class function Timestamp: TDataType.TTimestamp; static; inline; + class function Void: TDataType.TVoid; static; + class function Ordinal: TDataType.TOrdinal; static; + class function Float: TDataType.TFloat; static; + class function Text: TDataType.TText; static; + class function Timestamp: TDataType.TTimestamp; static; class function DecimalOf(const AScale: Integer): TDataType.TDecimal; static; class function TupleOf(const AItemTypes: array of IDataType): TDataType.TTuple; overload; static; class function TupleOf(const AItemValues: array of IDataValue): TDataType.TTuple.TValue; overload; static; diff --git a/dirs.txt b/dirs.txt index d60f15b..4a4515d 100644 --- a/dirs.txt +++ b/dirs.txt @@ -1,4 +1,4 @@ T:\Myc\Src T:\Myc\Src\Data +T:\Myc\Src\AST T:\Myc\Test -T:\Myc\AuraTrader \ No newline at end of file