Tail call optimization
This commit is contained in:
@@ -70,6 +70,7 @@ type
|
||||
InnerLambdaButton: TButton;
|
||||
DumpButton: TButton;
|
||||
FailingUpvalueButton: TButton;
|
||||
TailCallButten: TButton;
|
||||
procedure InnerLambdaButtonClick(Sender: TObject);
|
||||
procedure ClearButtonClick(Sender: TObject);
|
||||
procedure FormCreate(Sender: TObject);
|
||||
@@ -87,6 +88,7 @@ type
|
||||
procedure Test1ButtonClick(Sender: TObject);
|
||||
procedure Test2ButtonClick(Sender: TObject);
|
||||
procedure FromJSONButtonClick(Sender: TObject);
|
||||
procedure TailCallButtenClick(Sender: TObject);
|
||||
procedure ToJSONButtonClick(Sender: TObject);
|
||||
private
|
||||
// Stores the last AST generated by Test1 or Test2 button clicks.
|
||||
@@ -182,7 +184,10 @@ begin
|
||||
|
||||
resultValue := ExecuteAst(mainBlock, FGScope);
|
||||
|
||||
Assert(TScalar.FromInteger(15) = resultValue.AsScalar, 'The final result should be 15.');
|
||||
//TODO Dieses Assert löst aus:
|
||||
// "The final result should be 15, but is 10 (T:\Myc\ASTPlayground\MainForm.pas, line 186)"
|
||||
|
||||
Assert(TScalar.FromInteger(15) = resultValue.AsScalar, 'The final result should be 15, but is ' + resultValue.AsScalar.ToString);
|
||||
end;
|
||||
|
||||
procedure TForm1.ClearButtonClick(Sender: TObject);
|
||||
@@ -201,7 +206,7 @@ begin
|
||||
// It binds the AST and then decides whether to run a debug session or a standard evaluation.
|
||||
var scriptScope := TAstBinder.Bind(ANode, AParentScope).CreateScope(AParentScope);
|
||||
var visitor := CreateVisitor(scriptScope);
|
||||
Result := ANode.Accept(visitor);
|
||||
Result := visitor.Execute(ANode);
|
||||
end;
|
||||
|
||||
procedure TForm1.FormCreate(Sender: TObject);
|
||||
@@ -263,7 +268,7 @@ begin
|
||||
)
|
||||
);
|
||||
|
||||
Scope.Define('CreateSMA', smaAst.Accept(TEvaluatorVisitor.Create(TAstBinder.Bind(smaAst, FGScope).CreateScope(FGScope))));
|
||||
Scope.Define('CreateSMA', CreateVisitor(TAstBinder.Bind(smaAst, FGScope).CreateScope(FGScope)).Execute(smaAst));
|
||||
end
|
||||
);
|
||||
|
||||
@@ -331,7 +336,7 @@ begin
|
||||
end;
|
||||
|
||||
visitor := TPrettyPrintVisitor.Create;
|
||||
FLastAst.Accept(visitor);
|
||||
visitor.Execute(FLastAst);
|
||||
Memo1.Lines.Add(visitor.GetResult);
|
||||
end;
|
||||
|
||||
@@ -635,7 +640,7 @@ begin
|
||||
|
||||
// This is a temporary visitor just for the setup execution.
|
||||
var setupVisitor := CreateVisitor(scope);
|
||||
setupAst.Accept(setupVisitor);
|
||||
setupVisitor.Execute(setupAst);
|
||||
|
||||
// 2. Prepare for the simulation loop by modifying the now-populated scope.
|
||||
scope.Define('current_series', TDataValue.Void);
|
||||
@@ -685,7 +690,7 @@ begin
|
||||
if series.AsRecordSeries.TotalCount >= smaSlowLength then
|
||||
begin
|
||||
scope[seriesAddress] := series;
|
||||
var resultValue := callAst.Accept(visitor);
|
||||
var resultValue := visitor.Execute(callAst);
|
||||
if i mod 50 = 0 then
|
||||
begin
|
||||
Memo1.Lines.Add(Format('Tick %d/%d: Close = %.2f, Signal = %s', [i, numRecs, ohlcvRec.Close, resultValue.ToString]));
|
||||
@@ -726,7 +731,7 @@ begin
|
||||
|
||||
// This case is simple enough to just inline the logic from ExecuteAst
|
||||
var visitor := CreateVisitor(TriggerScope);
|
||||
blk.Accept(visitor);
|
||||
visitor.Execute(blk);
|
||||
|
||||
FLastAst := blk;
|
||||
|
||||
@@ -737,7 +742,7 @@ end;
|
||||
function TForm1.CreateVisitor(Scope: IExecutionScope): IAstVisitor;
|
||||
begin
|
||||
if DebugBox.IsChecked then
|
||||
Result := TDebugEvaluatorVisitor.CreateVisitor(Scope, Memo1.Lines, ShowScopeBox.IsChecked)
|
||||
Result := TDebugEvaluatorVisitor.Create(Scope, Memo1.Lines, ShowScopeBox.IsChecked)
|
||||
else
|
||||
Result := TEvaluatorVisitor.Create(Scope);
|
||||
end;
|
||||
@@ -906,6 +911,54 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TForm1.TailCallButtenClick(Sender: TObject);
|
||||
const
|
||||
// A large number to prove TCO prevents stack overflow
|
||||
RecursionDepth = 1000000;
|
||||
var
|
||||
root: IAstNode;
|
||||
result: TDataValue;
|
||||
sw: TStopwatch;
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add(Format('--- Testing TCO with recursion depth of %d ---', [RecursionDepth]));
|
||||
Application.ProcessMessages;
|
||||
sw := TStopwatch.StartNew;
|
||||
|
||||
root :=
|
||||
TAst.Block(
|
||||
[
|
||||
// var countDown = lambda(n) { ... };
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('countDown'),
|
||||
TAst.LambdaExpr(
|
||||
[TAst.Identifier('n')],
|
||||
// if (n > 0) then Self(n-1) else 'done'
|
||||
TAst.IfExpr(
|
||||
TAst.BinaryExpr(TAst.Identifier('n'), boGreater, TAst.Constant(TScalar.FromInt64(0))),
|
||||
// This is the tail call position.
|
||||
TAst.FunctionCall(
|
||||
TAst.Identifier('Self'),
|
||||
[TAst.BinaryExpr(TAst.Identifier('n'), boSubtract, TAst.Constant(TScalar.FromInt64(1)))]
|
||||
),
|
||||
// Base case of the recursion
|
||||
TAst.Constant(TScalar.FromString('done'))
|
||||
)
|
||||
)
|
||||
),
|
||||
// Initial call to start the recursion
|
||||
TAst.FunctionCall(TAst.Identifier('countDown'), [TAst.Constant(TScalar.FromInt64(RecursionDepth))])
|
||||
]
|
||||
);
|
||||
|
||||
FLastAst := root;
|
||||
result := ExecuteAst(root, FGScope);
|
||||
sw.Stop;
|
||||
|
||||
Memo1.Lines.Add(Format('Result: %s', [result.ToString]));
|
||||
Memo1.Lines.Add(Format('Execution finished in %d ms without stack overflow.', [sw.ElapsedMilliseconds]));
|
||||
end;
|
||||
|
||||
procedure TForm1.ToJSONButtonClick(Sender: TObject);
|
||||
var
|
||||
jsonString: string;
|
||||
|
||||
Reference in New Issue
Block a user