Files
MycLib/Src/AST/Myc.Ast.Debugger.pas
T
Michael Schimmel 991b998cb1 Tuples
2026-01-04 18:48:04 +01:00

197 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';
// NEW:
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.