AST refactoring

This commit is contained in:
Michael Schimmel
2025-08-29 00:27:17 +02:00
parent 5451f4fed9
commit 5593761551
6 changed files with 294 additions and 36 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<ProjectVersion>20.3</ProjectVersion>
<FrameworkType>FMX</FrameworkType>
<Base>True</Base>
<Config Condition="'$(Config)'==''">Release</Config>
<Config Condition="'$(Config)'==''">Debug</Config>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<ProjectName Condition="'$(ProjectName)'==''">ASTPlayground</ProjectName>
<TargetedPlatforms>3</TargetedPlatforms>
+25
View File
@@ -7,6 +7,8 @@ object Form1: TForm1
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
OnCreate = FormCreate
OnDestroy = FormDestroy
DesignerMasterStyle = 0
object Panel1: TPanel
Align = Left
@@ -71,6 +73,29 @@ object Form1: TForm1
TextSettings.Trimming = None
OnClick = FibonacciButtonClick
end
object CrerateTriggerExampleButton: TButton
Position.X = 24.000000000000000000
Position.Y = 320.000000000000000000
TabOrder = 9
Text = 'TriggerTest'
TextSettings.Trimming = None
OnClick = CrerateTriggerExampleButtonClick
end
object DoTriggerButton: TButton
Position.X = 24.000000000000000000
Position.Y = 350.000000000000000000
TabOrder = 10
Text = 'Trigger!'
TextSettings.Trimming = None
OnClick = DoTriggerButtonClick
object DoTrigger2Button: TButton
Position.Y = 30.000000000000000000
TabOrder = 8
Text = 'Trigger2'
TextSettings.Trimming = None
OnClick = DoTrigger2ButtonClick
end
end
end
object Memo1: TMemo
Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]
+129 -1
View File
@@ -34,7 +34,15 @@ type
RecursionButton: TButton;
ShowScopeBox: TCheckBox;
FibonacciButton: TButton;
CrerateTriggerExampleButton: TButton;
DoTriggerButton: TButton;
DoTrigger2Button: TButton;
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure CrerateTriggerExampleButtonClick(Sender: TObject);
procedure DebugButtonClick(Sender: TObject);
procedure DoTrigger2ButtonClick(Sender: TObject);
procedure DoTriggerButtonClick(Sender: TObject);
procedure FibonacciButtonClick(Sender: TObject);
procedure PrettyPrintButtonClick(Sender: TObject);
procedure RecursionButtonClick(Sender: TObject);
@@ -43,6 +51,7 @@ type
private
// Stores the last AST generated by Test1 or Test2 button clicks.
FLastAst: IAstNode;
FGScope: TExecutionScope;
public
{ Public declarations }
end;
@@ -57,6 +66,16 @@ uses
{$R *.fmx}
procedure TForm1.FormDestroy(Sender: TObject);
begin
FGScope.Free;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FGScope := TExecutionScope.Create(nil);
end;
procedure TForm1.DebugButtonClick(Sender: TObject);
var
scope: TExecutionScope;
@@ -71,7 +90,7 @@ begin
exit;
end;
scope := TExecutionScope.Create(nil);
scope := TExecutionScope.Create(FGScope);
try
Memo1.Lines.Clear;
Memo1.Lines.Add('--- Debug Evaluator Trace ---');
@@ -113,6 +132,8 @@ var
result20, result30, result40: Int64;
time20, time30, time40: Int64;
begin
FGScope.Clear;
Memo1.Lines.Clear;
Memo1.Lines.Add('--- Native Delphi Fibonacci Performance ---');
Memo1.Lines.Add('Calculating fib(30) and fib(40)...');
@@ -227,6 +248,8 @@ var
result: TAstValue;
sw: TStopwatch;
begin
FGScope.Clear;
sw := TStopwatch.Create;
Memo1.Lines.Clear;
Memo1.Lines.Add('--- Recursive factorial(20) ---');
@@ -280,6 +303,8 @@ var
result: TAstValue;
sw: TStopwatch;
begin
FGScope.Clear;
sw := TStopwatch.Create;
Memo1.Lines.Clear;
Memo1.Lines.Add('--- Simple AST Execution ---');
@@ -320,6 +345,8 @@ var
result: TAstValue;
sw: TStopwatch;
begin
FGScope.Clear;
sw := TStopwatch.Create;
Memo1.Lines.Clear;
Memo1.Lines.Add('--- Factory Pattern Demo ---');
@@ -361,4 +388,105 @@ begin
end;
end;
procedure TForm1.CrerateTriggerExampleButtonClick(Sender: TObject);
var
lambdaAst: ILambdaExpressionNode;
visitor: IAstVisitor;
closureValue: TAstValue;
begin
FGScope.Clear;
// Create an AST that gets a "tick" in DoTriggerButtonClick.
// This simulates a simple Blueprint-like event system.
// It maintains state via a persistent execution scope.
Memo1.Lines.Clear;
Memo1.Lines.Add('--- Creating Trigger Blueprint ---');
// 1. Initialize the variable 'X' to 0 directly in the scope.
FGScope.SetValue('X', TAstValue.FromScalar(TScalar.FromInt64(0)));
// 2. Create a lambda that takes the summand as an argument.
// AST for: (summand) => { X = X + summand; }
// Using the new Assignment node to modify 'X' in its parent scope.
lambdaAst :=
TAst.LambdaExpr(
[TAst.Identifier('summand')],
TAst.Assign(TAst.Identifier('X'), TAst.BinaryExpr(TAst.Identifier('X'), boAdd, TAst.Identifier('summand')))
);
// 3. Evaluate the lambda to create a closure and store it in the scope.
// The closure captures the scope where 'X' is defined.
visitor := TEvaluatorVisitor.Create(FGScope);
closureValue := lambdaAst.Accept(visitor);
FGScope.SetValue('tickHandler', closureValue);
// FLastAst is not used for this parameterized example.
FLastAst := lambdaAst;
Memo1.Lines.Add('Variable "X" initialized to 0 in persistent scope.');
Memo1.Lines.Add('Blueprint function "tickHandler(summand)" created.');
Memo1.Lines.Add('Click "Do Trigger" or "Do Trigger 2" to execute.');
end;
procedure TForm1.DoTriggerButtonClick(Sender: TObject);
var
visitor: IAstVisitor;
currentValue: TAstValue;
callAst: IFunctionCallNode;
begin
// A "tick" executes the stored AST using the persistent scope.
// Create an AST to call the stored handler with argument 1.
// AST for: tickHandler(1)
callAst := TAst.FunctionCall(TAst.Identifier('tickHandler'), [TAst.Constant(TScalar.FromInt64(1))]);
// Execute the call AST.
visitor := TEvaluatorVisitor.Create(FGScope);
callAst.Accept(visitor);
FLastAst := callAst;
// Get the updated value of 'X' from the scope and display it.
if FGScope.FindValue('X', currentValue) then
begin
Memo1.Lines.Add(Format('Tick(1)! New value of X: %s', [currentValue.ToString]));
end
else
begin
// This should not happen if setup was correct.
Memo1.Lines.Add('Error: Variable "X" not found in scope.');
end;
end;
procedure TForm1.DoTrigger2ButtonClick(Sender: TObject);
var
visitor: IAstVisitor;
currentValue: TAstValue;
callAst: IFunctionCallNode;
begin
// A "tick" that adds 2, using the same blueprint function.
// Create an AST to call the stored handler with argument 2.
// AST for: tickHandler(2)
callAst := TAst.FunctionCall(TAst.Identifier('tickHandler'), [TAst.Constant(TScalar.FromInt64(2))]);
FLastAst := callAst;
// Execute the call AST.
visitor := TEvaluatorVisitor.Create(FGScope);
callAst.Accept(visitor);
// Get the updated value of 'X' from the scope and display it.
if FGScope.FindValue('X', currentValue) then
begin
Memo1.Lines.Add(Format('Tick(2)! New value of X: %s', [currentValue.ToString]));
end
else
begin
// This should not happen if setup was correct.
Memo1.Lines.Add('Error: Variable "X" not found in scope.');
end;
end;
end.