310 lines
9.4 KiB
ObjectPascal
310 lines
9.4 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.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 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 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;
|
|
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<string>;
|
|
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.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
|
|
// 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;
|
|
|
|
end.
|