From 0f9eb36ab8c0aebfa133f5304d5d5312720f5565 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Sat, 13 Sep 2025 14:22:02 +0200 Subject: [PATCH] AST Debugger --- Src/AST/Myc.Ast.Debugger.pas | 352 +++++++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 Src/AST/Myc.Ast.Debugger.pas diff --git a/Src/AST/Myc.Ast.Debugger.pas b/Src/AST/Myc.Ast.Debugger.pas new file mode 100644 index 0000000..1f3ec2c --- /dev/null +++ b/Src/AST/Myc.Ast.Debugger.pas @@ -0,0 +1,352 @@ +unit Myc.Ast.Debugger; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Collections, + Myc.Data.Value, + Myc.Ast.Nodes, + Myc.Ast.Scope, + Myc.Ast, + Myc.Ast.Evaluator; + +type + TDebugSession = class + private + class var + FCurrent: TDebugSession; + private + FLog: TStrings; + FShowScope: Boolean; + FVisitorStack: TStack; + 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 read FVisitorStack; + end; + + 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. + function GetVisitorFactory: TVisitorFactory; override; + public + constructor Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0); + function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; + // The logging overrides for other Visit... methods + function VisitConstant(const Node: IConstantNode): TDataValue; override; + function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override; + function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override; + function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override; + function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; + function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; + 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 VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; + function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override; + function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override; + end; + +implementation + +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.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); +begin + inherited Create(AScope); + Assert(Assigned(ALog)); + FLog := ALog; + FIndentLevel := AInitialIndent; + FShowScope := AShowScope; + ShowScope; +end; + +function TDebugEvaluatorVisitor.GetVisitorFactory: TVisitorFactory; +begin + // Return a closure that creates a new Debug visitor, + // using the context from the currently active session stack. + 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); + end; +end; + +function TDebugEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; +begin + Assert(Assigned(TDebugSession.Current), 'No active debug session.'); + TDebugSession.Current.VisitorStack.Push(Self); + try + AppendLine('FunctionCall{'); + Indent; + try + ShowScope; + Result := inherited VisitFunctionCall(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.ToString])); + finally + TDebugSession.Current.VisitorStack.Pop; + end; +end; + +// ... The rest of the TDebugEvaluatorVisitor logging methods are unchanged ... +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 + begin + pad := pad + ':' + ''.PadLeft(3); + end; + FLog.Add(pad + S); +end; + +procedure TDebugEvaluatorVisitor.ShowScope; +var + scopeDump: TArray; + line: string; +begin + if FShowScope then + begin + AppendLine('-- Scope --'); + scopeDump := Scope.Dump.Split([sLineBreak]); + for line in scopeDump do + begin + AppendLine(line); + end; + AppendLine('-----------'); + end; +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.Identifier.Name])); + Indent; + try + Result := inherited VisitAssignment(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.ToString])); +end; + +function TDebugEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue; +begin + AppendLine(Format('Constant (%s)', [Node.Value.ToString])); + Result := inherited VisitConstant(Node); +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.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; +begin + AppendLine(Format('BinaryExpr "%s" {', [Node.Operator.ToString])); + Indent; + try + Result := inherited VisitBinaryExpression(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.ToString])); +end; + +function TDebugEvaluatorVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; +begin + AppendLine(Format('UnaryExpr "%s" {', [Node.Operator.ToString])); + Indent; + try + Result := inherited VisitUnaryExpression(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [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.Name])); + Indent; + try + Result := inherited VisitMemberAccess(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.ToString])); +end; + +function TDebugEvaluatorVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; +begin + AppendLine('TernaryExpr{'); + Indent; + try + Result := inherited VisitTernaryExpression(Node); + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.ToString])); +end; + +function TDebugEvaluatorVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; +begin + AppendLine('Block{'); + Indent; + try + Result := inherited VisitBlockExpression(Node); + ShowScope; + finally + Unindent; + end; + AppendLine(Format('} -> %s', [Result.ToString])); +end; + +function TDebugEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; +begin + AppendLine(Format('VarDecl %s :=', [Node.Identifier.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; + +end.