195 lines
5.8 KiB
ObjectPascal
195 lines
5.8 KiB
ObjectPascal
unit Myc.Ast.Debugger;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
System.Classes,
|
|
System.Generics.Collections,
|
|
Myc.Data.Scalar,
|
|
Myc.Data.Value,
|
|
Myc.Ast.Nodes,
|
|
Myc.Ast.Scope,
|
|
Myc.Ast,
|
|
Myc.Ast.Visitor,
|
|
Myc.Ast.Evaluator;
|
|
|
|
type
|
|
TDebugEvaluatorVisitor = class(TEvaluatorVisitor)
|
|
private
|
|
FLog: TStrings;
|
|
FIndentLevel: Integer;
|
|
FShowScope: Boolean;
|
|
|
|
procedure Indent;
|
|
procedure Unindent;
|
|
procedure AppendLine(const S: string);
|
|
procedure ShowScope;
|
|
function GetNodeLogInfo(const Node: IAstNode): string;
|
|
protected
|
|
function CreateVisitorFactory: TEvaluatorFactory; override;
|
|
|
|
// Central Interceptor for all node types
|
|
function Visit(const Node: IAstNode): TDataValue; override;
|
|
|
|
// Specific overrides for enhanced logging/state observation
|
|
function VisitVariableDeclaration(const N: IVariableDeclarationNode): TDataValue; override;
|
|
function VisitAssignment(const N: IAssignmentNode): TDataValue; override;
|
|
function VisitFunctionCall(const N: IFunctionCallNode): TDataValue; override;
|
|
function VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue; override;
|
|
public
|
|
constructor Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0);
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.TypInfo,
|
|
Myc.Data.Keyword;
|
|
|
|
{ TDebugEvaluatorVisitor }
|
|
|
|
constructor TDebugEvaluatorVisitor.Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer);
|
|
begin
|
|
inherited Create(AScope);
|
|
FLog := ALog;
|
|
FIndentLevel := AInitialIndent;
|
|
FShowScope := AShowScope;
|
|
if FShowScope and (AInitialIndent = 0) then
|
|
ShowScope;
|
|
end;
|
|
|
|
function TDebugEvaluatorVisitor.CreateVisitorFactory: TEvaluatorFactory;
|
|
begin
|
|
var currentLog := FLog;
|
|
var currentShowScope := FShowScope;
|
|
var currentIndent := FIndentLevel;
|
|
Result :=
|
|
function(const AScope: IExecutionScope): IEvaluatorVisitor
|
|
begin
|
|
Result := TDebugEvaluatorVisitor.Create(AScope, currentLog, currentShowScope, currentIndent);
|
|
end;
|
|
end;
|
|
|
|
function TDebugEvaluatorVisitor.Visit(const Node: IAstNode): TDataValue;
|
|
var
|
|
info: string;
|
|
begin
|
|
info := GetNodeLogInfo(Node);
|
|
if info <> '' then
|
|
begin
|
|
AppendLine(info + ' {');
|
|
Indent;
|
|
end;
|
|
|
|
try
|
|
// Dispatch to inherited logic (which will call our specialized VisitXyz overrides)
|
|
Result := inherited Visit(Node);
|
|
finally
|
|
if info <> '' then
|
|
begin
|
|
Unindent;
|
|
var resStr :=
|
|
if Result.IsVoid then '(void)'
|
|
else Result.ToString;
|
|
AppendLine('} -> ' + resStr);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// --- Enhanced Observation Overrides ---
|
|
|
|
function TDebugEvaluatorVisitor.VisitVariableDeclaration(const N: IVariableDeclarationNode): TDataValue;
|
|
begin
|
|
Result := inherited VisitVariableDeclaration(N);
|
|
if FShowScope then
|
|
ShowScope;
|
|
end;
|
|
|
|
function TDebugEvaluatorVisitor.VisitAssignment(const N: IAssignmentNode): TDataValue;
|
|
begin
|
|
Result := inherited VisitAssignment(N);
|
|
if FShowScope then
|
|
ShowScope;
|
|
end;
|
|
|
|
function TDebugEvaluatorVisitor.VisitFunctionCall(const N: IFunctionCallNode): TDataValue;
|
|
begin
|
|
// Log target purity if statically known
|
|
if Assigned(N.StaticTarget) and N.IsTargetPure then
|
|
AppendLine('[Pure Static Call]');
|
|
Result := inherited VisitFunctionCall(N);
|
|
end;
|
|
|
|
function TDebugEvaluatorVisitor.VisitLambdaExpression(const N: ILambdaExpressionNode): TDataValue;
|
|
begin
|
|
if Length(N.Upvalues) > 0 then
|
|
AppendLine(Format('[Capturing %d upvalues]', [Length(N.Upvalues)]));
|
|
Result := inherited VisitLambdaExpression(N);
|
|
end;
|
|
|
|
// --- Internal Helpers ---
|
|
|
|
function TDebugEvaluatorVisitor.GetNodeLogInfo(const Node: IAstNode): string;
|
|
begin
|
|
case Node.Kind of
|
|
akFunctionCall:
|
|
begin
|
|
var c := Node.AsFunctionCall;
|
|
var mode :=
|
|
if Assigned(c.StaticTarget) then 'STATIC'
|
|
else 'DYNAMIC';
|
|
Result := Format('Call (%s, Tail=%s)', [mode, c.IsTailCall.ToString(TUseBoolStrs.True)]);
|
|
end;
|
|
akIdentifier: Result := 'ID:' + Node.AsIdentifier.Name;
|
|
akVariableDeclaration: Result := 'DEF:' + Node.AsVariableDeclaration.Target.AsIdentifier.Name;
|
|
akAssignment: Result := 'ASSIGN:' + Node.AsAssignment.Target.AsIdentifier.Name;
|
|
akIfExpression: Result := 'IF';
|
|
akCondExpression: Result := 'COND';
|
|
akRecur: Result := 'RECUR';
|
|
akBlockExpression: Result := 'BLOCK';
|
|
akLambdaExpression: Result := 'FN';
|
|
akRecordLiteral: Result := 'RECORD';
|
|
akPipe: Result := 'PIPE';
|
|
akIndexer: Result := 'INDEXER';
|
|
akMemberAccess: Result := 'MEMBER';
|
|
akTuple: Result := 'TUPLE';
|
|
akCreateSeries: Result := 'NEW-SERIES';
|
|
akAddSeriesItem: Result := 'ADD-ITEM';
|
|
|
|
else
|
|
Result := ''; // Silence leaf nodes like Constants and Keywords to reduce noise
|
|
end;
|
|
end;
|
|
|
|
procedure TDebugEvaluatorVisitor.Indent;
|
|
begin
|
|
Inc(FIndentLevel);
|
|
end;
|
|
procedure TDebugEvaluatorVisitor.Unindent;
|
|
begin
|
|
Dec(FIndentLevel);
|
|
end;
|
|
|
|
procedure TDebugEvaluatorVisitor.AppendLine(const S: string);
|
|
var
|
|
pad: string;
|
|
i: Integer;
|
|
begin
|
|
pad := '';
|
|
for i := 0 to FIndentLevel - 1 do
|
|
pad := pad + ':' + ''.PadLeft(3);
|
|
FLog.Add(pad + S);
|
|
end;
|
|
|
|
procedure TDebugEvaluatorVisitor.ShowScope;
|
|
var
|
|
line: string;
|
|
begin
|
|
AppendLine(' [Scope State]');
|
|
for line in Scope.Dump.Split([sLineBreak]) do
|
|
AppendLine(' ' + line);
|
|
end;
|
|
|
|
end.
|