AST-Playground
This commit is contained in:
@@ -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('<no result>');
|
||||
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.
|
||||
Reference in New Issue
Block a user