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.