AST Debugger
This commit is contained in:
+61
-121
@@ -31,7 +31,7 @@ uses
|
||||
FMX.Layouts,
|
||||
FMX.Objects,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast.Debugger; // Added for TDebugSession
|
||||
Myc.Ast.Debugger;
|
||||
|
||||
type
|
||||
// A test record
|
||||
@@ -86,6 +86,7 @@ type
|
||||
FGScope: IExecutionScope;
|
||||
FWorkspace: TAuraWorkspace;
|
||||
procedure WorkspaceMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
|
||||
function CreateVisitor(Scope: IExecutionScope): IAstVisitor;
|
||||
// Helper function to encapsulate the Bind -> Evaluate pattern
|
||||
function ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue;
|
||||
public
|
||||
@@ -116,31 +117,12 @@ begin
|
||||
end;
|
||||
|
||||
function TForm1.ExecuteAst(const ANode: IAstNode; const AParentScope: IExecutionScope): TDataValue;
|
||||
var
|
||||
scriptScope: IExecutionScope;
|
||||
begin
|
||||
// This helper function handles simple, one-off script executions.
|
||||
// It binds the AST and then decides whether to run a debug session or a standard evaluation.
|
||||
scriptScope := TAst.Bind(ANode, AParentScope);
|
||||
|
||||
if DebugBox.IsChecked then
|
||||
begin
|
||||
// Debug path: Create, run, and destroy a debug session.
|
||||
var session := TDebugSession.Create(ShowScopeBox.IsChecked);
|
||||
try
|
||||
Result := session.Execute(ANode, scriptScope);
|
||||
Memo1.Lines.Add('--- DEBUG LOG ---');
|
||||
Memo1.Lines.AddStrings(session.Log);
|
||||
finally
|
||||
session.Free;
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Production path: Create and use the standard evaluator.
|
||||
var visitor := TEvaluatorVisitor.Create(scriptScope);
|
||||
Result := ANode.Accept(visitor);
|
||||
end;
|
||||
var scriptScope := TAst.Bind(ANode, AParentScope);
|
||||
var visitor := CreateVisitor(scriptScope);
|
||||
Result := ANode.Accept(visitor);
|
||||
end;
|
||||
|
||||
procedure TForm1.FormCreate(Sender: TObject);
|
||||
@@ -407,9 +389,7 @@ const
|
||||
smaSlowLength = 20;
|
||||
smaFastLength = 10;
|
||||
var
|
||||
visitor: IAstVisitor;
|
||||
scope: IExecutionScope;
|
||||
session: TDebugSession;
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add(Format('--- Simulating O(1) SMA Crossover Strategy for %d ticks ---', [numRecs]));
|
||||
@@ -518,25 +498,8 @@ begin
|
||||
scope := TAst.Bind(setupAst, FGScope);
|
||||
|
||||
// This is a temporary visitor just for the setup execution.
|
||||
var setupVisitor: IAstVisitor;
|
||||
if DebugBox.IsChecked then
|
||||
begin
|
||||
session := TDebugSession.Create(ShowScopeBox.IsChecked);
|
||||
try
|
||||
// Execute setup script within a temporary session.
|
||||
session.Execute(setupAst, scope);
|
||||
Memo1.Lines.Add('--- DEBUG LOG (Setup) ---');
|
||||
Memo1.Lines.AddStrings(session.Log);
|
||||
session.Log.Clear; // Clear log for the next phase
|
||||
finally
|
||||
session.Free;
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
setupVisitor := TEvaluatorVisitor.Create(scope);
|
||||
setupAst.Accept(setupVisitor);
|
||||
end;
|
||||
var setupVisitor := CreateVisitor(scope);
|
||||
setupAst.Accept(setupVisitor);
|
||||
|
||||
// 2. Prepare for the simulation loop by modifying the now-populated scope.
|
||||
scope.Define('current_series', TDataValue.Void);
|
||||
@@ -547,73 +510,57 @@ begin
|
||||
scope := TAst.Bind(callAst, scope);
|
||||
var seriesAddress := currentSeriesIdent.Address;
|
||||
|
||||
// 4. NOW create the visitor for the simulation loop, using the final scope.
|
||||
session := nil;
|
||||
try
|
||||
if DebugBox.IsChecked then
|
||||
begin
|
||||
// Create a new session that will live for the duration of the loop.
|
||||
session := TDebugSession.Create(ShowScopeBox.IsChecked);
|
||||
visitor := TDebugEvaluatorVisitor.Create(scope, session.Log, ShowScopeBox.IsChecked, 1);
|
||||
end
|
||||
else
|
||||
begin
|
||||
visitor := TEvaluatorVisitor.Create(scope);
|
||||
end;
|
||||
var visitor := CreateVisitor(scope);
|
||||
|
||||
// 5. Simulation Loop
|
||||
Memo1.Lines.Add('Starting simulation...');
|
||||
Application.ProcessMessages;
|
||||
// 5. Simulation Loop
|
||||
Memo1.Lines.Add('Starting simulation...');
|
||||
Application.ProcessMessages;
|
||||
|
||||
var lastClose := 1000.0;
|
||||
Randomize;
|
||||
var recDef := TRttiAstHelper.JsonToRecordDefinition(TRttiAstHelper.RecordDefinitionToJson<TOHLCV>);
|
||||
var series := TDataValue.FromRecordSeries(TScalarRecordSeries.Create(recDef));
|
||||
var nw := Now;
|
||||
var ohlcvRec: TOHLCV;
|
||||
for var i := 1 to numRecs do
|
||||
var lastClose := 1000.0;
|
||||
Randomize;
|
||||
var recDef := TRttiAstHelper.JsonToRecordDefinition(TRttiAstHelper.RecordDefinitionToJson<TOHLCV>);
|
||||
var series := TDataValue.FromRecordSeries(TScalarRecordSeries.Create(recDef));
|
||||
var nw := Now;
|
||||
var ohlcvRec: TOHLCV;
|
||||
for var i := 1 to numRecs do
|
||||
begin
|
||||
// ... (simulation logic is unchanged) ...
|
||||
ohlcvRec.Timestamp := nw + (i / (24 * 60));
|
||||
ohlcvRec.Open := lastClose + (Random * 0.05);
|
||||
ohlcvRec.Close := lastClose + (Random - 0.49) * 2;
|
||||
ohlcvRec.High := Max(ohlcvRec.Open, ohlcvRec.Close) + Random;
|
||||
ohlcvRec.Low := Min(ohlcvRec.Open, ohlcvRec.Close) - Random;
|
||||
ohlcvRec.Volume := RandomRange(1000, 50000);
|
||||
lastClose := ohlcvRec.Close;
|
||||
var recordValue :=
|
||||
TScalarRecord.Create(
|
||||
recDef,
|
||||
[
|
||||
TScalarValue.FromDateTime(ohlcvRec.Timestamp),
|
||||
TScalarValue.FromDouble(ohlcvRec.Open),
|
||||
TScalarValue.FromDouble(ohlcvRec.High),
|
||||
TScalarValue.FromDouble(ohlcvRec.Low),
|
||||
TScalarValue.FromDouble(ohlcvRec.Close),
|
||||
TScalarValue.FromInt64(ohlcvRec.Volume)
|
||||
]
|
||||
);
|
||||
series.AsRecordSeries.Value.Add(recordValue, lookback);
|
||||
|
||||
if series.AsRecordSeries.Value.TotalCount >= smaSlowLength then
|
||||
begin
|
||||
// ... (simulation logic is unchanged) ...
|
||||
ohlcvRec.Timestamp := nw + (i / (24 * 60));
|
||||
ohlcvRec.Open := lastClose + (Random * 0.05);
|
||||
ohlcvRec.Close := lastClose + (Random - 0.49) * 2;
|
||||
ohlcvRec.High := Max(ohlcvRec.Open, ohlcvRec.Close) + Random;
|
||||
ohlcvRec.Low := Min(ohlcvRec.Open, ohlcvRec.Close) - Random;
|
||||
ohlcvRec.Volume := RandomRange(1000, 50000);
|
||||
lastClose := ohlcvRec.Close;
|
||||
var recordValue :=
|
||||
TScalarRecord.Create(
|
||||
recDef,
|
||||
[
|
||||
TScalarValue.FromDateTime(ohlcvRec.Timestamp),
|
||||
TScalarValue.FromDouble(ohlcvRec.Open),
|
||||
TScalarValue.FromDouble(ohlcvRec.High),
|
||||
TScalarValue.FromDouble(ohlcvRec.Low),
|
||||
TScalarValue.FromDouble(ohlcvRec.Close),
|
||||
TScalarValue.FromInt64(ohlcvRec.Volume)
|
||||
]
|
||||
);
|
||||
series.AsRecordSeries.Value.Add(recordValue, lookback);
|
||||
|
||||
if series.AsRecordSeries.Value.TotalCount >= smaSlowLength then
|
||||
scope[seriesAddress].Value := series;
|
||||
var resultValue := callAst.Accept(visitor);
|
||||
if i mod 50 = 0 then
|
||||
begin
|
||||
scope[seriesAddress].Value := series;
|
||||
var resultValue := callAst.Accept(visitor);
|
||||
if i mod 50 = 0 then
|
||||
begin
|
||||
Memo1.Lines.Add(Format('Tick %d/%d: Close = %.2f, Signal = %s', [i, numRecs, ohlcvRec.Close, resultValue.ToString]));
|
||||
Application.ProcessMessages;
|
||||
end;
|
||||
Memo1.Lines.Add(Format('Tick %d/%d: Close = %.2f, Signal = %s', [i, numRecs, ohlcvRec.Close, resultValue.ToString]));
|
||||
Application.ProcessMessages;
|
||||
end;
|
||||
end;
|
||||
|
||||
sw.Stop;
|
||||
Memo1.Lines.Add('--- Simulation Finished ---');
|
||||
Memo1.Lines.Add(Format('Total time: %d ms', [sw.ElapsedMilliseconds]));
|
||||
finally
|
||||
if Assigned(session) then
|
||||
session.Free;
|
||||
end;
|
||||
|
||||
sw.Stop;
|
||||
Memo1.Lines.Add('--- Simulation Finished ---');
|
||||
Memo1.Lines.Add(Format('Total time: %d ms', [sw.ElapsedMilliseconds]));
|
||||
end;
|
||||
|
||||
var
|
||||
@@ -622,7 +569,6 @@ var
|
||||
procedure TForm1.CreateTriggerExampleButtonClick(Sender: TObject);
|
||||
var
|
||||
blk: IAstNode;
|
||||
visitor: IAstVisitor;
|
||||
begin
|
||||
Memo1.Lines.Clear;
|
||||
Memo1.Lines.Add('--- Creating Trigger Blueprint ---');
|
||||
@@ -643,22 +589,8 @@ begin
|
||||
TriggerScope := TAst.Bind(blk, FGScope);
|
||||
|
||||
// This case is simple enough to just inline the logic from ExecuteAst
|
||||
if DebugBox.IsChecked then
|
||||
begin
|
||||
var session := TDebugSession.Create(ShowScopeBox.IsChecked);
|
||||
try
|
||||
session.Execute(blk, TriggerScope);
|
||||
Memo1.Lines.Add('--- DEBUG LOG ---');
|
||||
Memo1.Lines.AddStrings(session.Log);
|
||||
finally
|
||||
session.Free;
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
visitor := TEvaluatorVisitor.Create(TriggerScope);
|
||||
blk.Accept(visitor);
|
||||
end;
|
||||
var visitor := CreateVisitor(TriggerScope);
|
||||
blk.Accept(visitor);
|
||||
|
||||
FLastAst := blk;
|
||||
|
||||
@@ -666,6 +598,14 @@ begin
|
||||
Memo1.Lines.Add('Click "Do Trigger" to execute.');
|
||||
end;
|
||||
|
||||
function TForm1.CreateVisitor(Scope: IExecutionScope): IAstVisitor;
|
||||
begin
|
||||
if DebugBox.IsChecked then
|
||||
Result := TDebugEvaluatorVisitor.CreateVisitor(Scope, Memo1.Lines, ShowScopeBox.IsChecked)
|
||||
else
|
||||
Result := TEvaluatorVisitor.Create(Scope);
|
||||
end;
|
||||
|
||||
procedure TForm1.DoTriggerButtonClick(Sender: TObject);
|
||||
var
|
||||
callAst: IFunctionCallNode;
|
||||
|
||||
Reference in New Issue
Block a user