AST Debugger

This commit is contained in:
Michael Schimmel
2025-09-13 20:40:18 +02:00
parent 0f9eb36ab8
commit d731048b41
4 changed files with 143 additions and 206 deletions
+33 -80
View File
@@ -13,26 +13,6 @@ uses
Myc.Ast.Evaluator;
type
TDebugSession = class
private
class var
FCurrent: TDebugSession;
private
FLog: TStrings;
FShowScope: Boolean;
FVisitorStack: TStack<IAstVisitor>;
function GetLog: TStrings;
public
constructor Create(AShowScope: Boolean);
destructor Destroy; override;
class property Current: TDebugSession read FCurrent;
function Execute(const ANode: IAstNode; const AScriptScope: IExecutionScope): TDataValue;
property Log: TStrings read GetLog;
property VisitorStack: TStack<IAstVisitor> read FVisitorStack;
end;
TDebugEvaluatorVisitor = class(TEvaluatorVisitor)
private
FLog: TStrings;
@@ -44,9 +24,18 @@ type
procedure ShowScope;
protected
// Overridden to provide the debug-specific visitor factory.
function GetVisitorFactory: TVisitorFactory; override;
function CreateVisitorFactory: TVisitorFactory; override;
public
constructor Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0);
class function CreateVisitor(
const AScope: IExecutionScope;
ALog: TStrings;
AShowScope: Boolean;
AInitialIndent: Integer = 0
): IAstVisitor;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override;
// The logging overrides for other Visit... methods
function VisitConstant(const Node: IConstantNode): TDataValue; override;
@@ -71,44 +60,9 @@ uses
System.TypInfo,
Myc.Ast.Printer;
{ TDebugSession }
constructor TDebugSession.Create(AShowScope: Boolean);
begin
inherited Create;
if Assigned(FCurrent) then
raise EInvalidOpException.Create('Another debug session is already active.');
FLog := TStringList.Create;
FShowScope := AShowScope;
FVisitorStack := TStack<IAstVisitor>.Create;
FCurrent := Self;
end;
destructor TDebugSession.Destroy;
begin
FLog.Free;
FVisitorStack.Free;
FCurrent := nil;
inherited Destroy;
end;
function TDebugSession.Execute(const ANode: IAstNode; const AScriptScope: IExecutionScope): TDataValue;
var
initialVisitor: IAstVisitor;
begin
initialVisitor := TDebugEvaluatorVisitor.Create(AScriptScope, FLog, FShowScope, 0);
Result := ANode.Accept(initialVisitor);
end;
function TDebugSession.GetLog: TStrings;
begin
Result := FLog;
end;
{ TDebugEvaluatorVisitor }
constructor TDebugEvaluatorVisitor.Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer);
constructor TDebugEvaluatorVisitor.Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0);
begin
inherited Create(AScope);
Assert(Assigned(ALog));
@@ -118,41 +72,32 @@ begin
ShowScope;
end;
function TDebugEvaluatorVisitor.GetVisitorFactory: TVisitorFactory;
function TDebugEvaluatorVisitor.CreateVisitorFactory: TVisitorFactory;
begin
// Return a closure that creates a new Debug visitor,
// using the context from the currently active session stack.
// Return a closure that creates a new Debug visitor, using the current context
var callingVisitor := Self as IAstVisitor;
Result :=
function(const AScope: IExecutionScope): IAstVisitor
begin
if (not Assigned(TDebugSession.Current)) or (TDebugSession.Current.VisitorStack.Count = 0) then
raise EInvalidOpException.Create('Visitor stack is empty during function invocation.');
var callingVisitor := TDebugSession.Current.VisitorStack.Peek as TDebugEvaluatorVisitor;
Result := TDebugEvaluatorVisitor.Create(AScope, callingVisitor.FLog, callingVisitor.FShowScope, callingVisitor.FIndentLevel);
var visitor := callingVisitor as TDebugEvaluatorVisitor;
Result := TDebugEvaluatorVisitor.Create(AScope, visitor.FLog, visitor.FShowScope, visitor.FIndentLevel);
end;
end;
function TDebugEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
begin
Assert(Assigned(TDebugSession.Current), 'No active debug session.');
TDebugSession.Current.VisitorStack.Push(Self);
AppendLine('FunctionCall{');
Indent;
try
AppendLine('FunctionCall{');
Indent;
try
ShowScope;
Result := inherited VisitFunctionCall(Node);
finally
Unindent;
end;
AppendLine(Format('} -> %s', [Result.ToString]));
ShowScope;
Result := inherited VisitFunctionCall(Node);
finally
TDebugSession.Current.VisitorStack.Pop;
Unindent;
end;
AppendLine(Format('} -> %s', [Result.ToString]));
end;
// ... The rest of the TDebugEvaluatorVisitor logging methods are unchanged ...
procedure TDebugEvaluatorVisitor.Indent;
begin
inc(FIndentLevel);
@@ -170,12 +115,20 @@ var
begin
pad := '';
for i := 0 to FIndentLevel - 1 do
begin
pad := pad + ':' + ''.PadLeft(3);
end;
FLog.Add(pad + S);
end;
class function TDebugEvaluatorVisitor.CreateVisitor(
const AScope: IExecutionScope;
ALog: TStrings;
AShowScope: Boolean;
AInitialIndent: Integer = 0
): IAstVisitor;
begin
Result := TDebugEvaluatorVisitor.Create(AScope, ALog, AShowScope, AInitialIndent);
end;
procedure TDebugEvaluatorVisitor.ShowScope;
var
scopeDump: TArray<string>;