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.Evaluator; type TDebugEvaluatorVisitor = class(TEvaluatorVisitor) private FLog: TStrings; FIndentLevel: Integer; FShowScope: Boolean; procedure Indent; procedure Unindent; procedure AppendLine(const S: string); procedure ShowScope; protected // Overridden to provide the debug-specific visitor factory for nested calls (Lambdas). function CreateVisitorFactory: TEvaluatorFactory; override; public constructor Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0); // The logging overrides for Visit... methods function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitConstant(const Node: IConstantNode): TDataValue; override; function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; function VisitKeyword(const Node: IKeywordNode): TDataValue; override; function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitCondExpression(const Node: ICondExpressionNode): TDataValue; override; // Replaces Ternary function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; function VisitIndexer(const Node: IIndexerNode): TDataValue; override; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; function VisitNop(const Node: INopNode): TDataValue; override; // Pipe Support function VisitPipeInput(const Node: IPipeInputNode): TDataValue; override; function VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; override; function VisitPipeInputList(const Node: IPipeInputList): TDataValue; override; function VisitPipe(const Node: IPipeNode): TDataValue; override; end; implementation uses System.TypInfo, Myc.Data.Keyword; { TDebugEvaluatorVisitor } constructor TDebugEvaluatorVisitor.Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0); begin inherited Create(AScope); Assert(Assigned(ALog)); FLog := ALog; FIndentLevel := AInitialIndent; FShowScope := AShowScope; ShowScope; end; function TDebugEvaluatorVisitor.CreateVisitorFactory: TEvaluatorFactory; begin // Return a closure that creates a new Debug visitor, ensuring the debug context (Log, Indent) is passed down. // This is used by TEvaluatorVisitor.VisitLambdaExpression when executing the closure body. // Capture current state var currentLog := FLog; var currentShowScope := FShowScope; var currentIndent := FIndentLevel; // Pass current indent as base for new frame Result := function(const AScope: IExecutionScope): IEvaluatorVisitor begin Result := TDebugEvaluatorVisitor.Create(AScope, currentLog, currentShowScope, currentIndent); 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 scopeDump: TArray; line: string; begin if FShowScope then begin AppendLine('-- Scope --'); // Scope.Dump logic has been updated in Myc.Ast.Scope to handle new architecture scopeDump := Scope.Dump.Split([sLineBreak]); for line in scopeDump do begin AppendLine(line); end; AppendLine('-----------'); end; end; // --- Visit Overrides --- function TDebugEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; var mode: string; begin if Assigned(Node.StaticTarget) then mode := 'STATIC' else mode := 'DYNAMIC'; AppendLine(Format('FunctionCall (%s, Tail=%s) {', [mode, Node.IsTailCall.ToString(TUseBoolStrs.True)])); Indent; try // ShowScope is called in Constructor of new Visitor (via CreateVisitorFactory) for the body, // but for arguments evaluation here in the current scope, we might want to see it. ShowScope; Result := inherited VisitFunctionCall(Node); finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; begin AppendLine('AddSeriesItem {'); Indent; try Result := inherited VisitAddSeriesItem(Node); finally Unindent; end; AppendLine('} -> (void)'); end; function TDebugEvaluatorVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue; begin AppendLine(Format('Assignment to "%s" {', [Node.Target.AsIdentifier.Name])); Indent; try Result := inherited VisitAssignment(Node); finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue; begin Result := inherited VisitConstant(Node); AppendLine(Format('Constant (%s)', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue; begin Result := inherited VisitKeyword(Node); AppendLine(Format('Keyword (%s)', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; begin AppendLine('CreateSeries {'); Indent; try Result := inherited VisitCreateSeries(Node); finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue; begin Result := inherited VisitIdentifier(Node); AppendLine(Format('Identifier "%s" -> %s', [Node.Name, Result.ToString])); end; function TDebugEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; begin AppendLine('IfExpr {'); Indent; try Result := inherited VisitIfExpression(Node); finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue; begin AppendLine('Indexer {'); Indent; try Result := inherited VisitIndexer(Node); finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; begin AppendLine(Format('MemberAccess (Member: %s) {', [Node.Member.Value.Name])); Indent; try Result := inherited VisitMemberAccess(Node); finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; begin AppendLine('RecordLiteral {'); Indent; try Result := inherited VisitRecordLiteral(Node); finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitCondExpression(const Node: ICondExpressionNode): TDataValue; begin AppendLine(Format('CondExpr (%d pairs) {', [Length(Node.Pairs)])); Indent; try // We just call inherited, which does the logic. // We don't log every branch check here to avoid spamming output, // but the recursive evaluation of conditions will show up in the log naturally. Result := inherited VisitCondExpression(Node); finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; begin AppendLine('Block {'); Indent; try // Delegates to VisitExpressionList via inherited Result := inherited VisitBlockExpression(Node); // Scope might have changed after block execution if vars were defined ShowScope; finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; begin AppendLine(Format('VarDecl %s :=', [Node.Target.AsIdentifier.Name])); Indent; try Result := inherited VisitVariableDeclaration(Node); finally Unindent; end; end; function TDebugEvaluatorVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; begin AppendLine('SeriesLength {'); Indent; try Result := inherited VisitSeriesLength(Node); finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitNop(const Node: INopNode): TDataValue; begin AppendLine('Nop (void)'); Result := TDataValue.Void; end; // --- Pipe Support --- function TDebugEvaluatorVisitor.VisitPipe(const Node: IPipeNode): TDataValue; begin AppendLine('Pipe {'); Indent; try Result := inherited VisitPipe(Node); finally Unindent; end; AppendLine(Format('} -> %s', [Result.ToString])); end; function TDebugEvaluatorVisitor.VisitPipeInput(const Node: IPipeInputNode): TDataValue; begin AppendLine(Format('PipeInput (Source: %s) {', [Node.StreamSource.Name])); Indent; try Result := inherited VisitPipeInput(Node); finally Unindent; end; AppendLine('}'); end; function TDebugEvaluatorVisitor.VisitPipeSelectorList(const Node: IPipeSelectorList): TDataValue; begin AppendLine('Selectors ['); Indent; try Result := inherited VisitPipeSelectorList(Node); finally Unindent; end; AppendLine(']'); end; function TDebugEvaluatorVisitor.VisitPipeInputList(const Node: IPipeInputList): TDataValue; begin AppendLine('Inputs ['); Indent; try Result := inherited VisitPipeInputList(Node); finally Unindent; end; AppendLine(']'); end; end.