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.