Generic Visitors
This commit is contained in:
+105
-285
@@ -11,6 +11,7 @@ uses
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast,
|
||||
Myc.Ast.Visitor,
|
||||
Myc.Ast.Evaluator;
|
||||
|
||||
type
|
||||
@@ -19,41 +20,25 @@ type
|
||||
FLog: TStrings;
|
||||
FIndentLevel: Integer;
|
||||
FShowScope: Boolean;
|
||||
|
||||
procedure Indent;
|
||||
procedure Unindent;
|
||||
procedure AppendLine(const S: string);
|
||||
procedure ShowScope;
|
||||
|
||||
function GetNodeLogInfo(const Node: IAstNode): string;
|
||||
protected
|
||||
// Overridden to provide the debug-specific visitor factory for nested calls (Lambdas).
|
||||
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);
|
||||
|
||||
// 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
|
||||
@@ -64,26 +49,21 @@ uses
|
||||
|
||||
{ TDebugEvaluatorVisitor }
|
||||
|
||||
constructor TDebugEvaluatorVisitor.Create(const AScope: IExecutionScope; ALog: TStrings; AShowScope: Boolean; AInitialIndent: Integer = 0);
|
||||
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;
|
||||
if FShowScope and (AInitialIndent = 0) then
|
||||
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
|
||||
|
||||
var currentIndent := FIndentLevel;
|
||||
Result :=
|
||||
function(const AScope: IExecutionScope): IEvaluatorVisitor
|
||||
begin
|
||||
@@ -91,14 +71,98 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDebugEvaluatorVisitor.Indent;
|
||||
function TDebugEvaluatorVisitor.Visit(const Node: IAstNode): TDataValue;
|
||||
var
|
||||
info: string;
|
||||
begin
|
||||
inc(FIndentLevel);
|
||||
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';
|
||||
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);
|
||||
Dec(FIndentLevel);
|
||||
end;
|
||||
|
||||
procedure TDebugEvaluatorVisitor.AppendLine(const S: string);
|
||||
@@ -114,255 +178,11 @@ 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.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(']');
|
||||
AppendLine(' [Scope State]');
|
||||
for line in Scope.Dump.Split([sLineBreak]) do
|
||||
AppendLine(' ' + line);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user