Files
MycLib/Src/AST/Myc.Ast.Dumper.pas
T
Michael Schimmel d12c6c966c Fix in Upvalue-Logic
2025-09-18 20:11:12 +02:00

328 lines
9.1 KiB
ObjectPascal

unit Myc.Ast.Dumper;
interface
uses
System.SysUtils,
System.Classes,
System.Generics.Collections,
Myc.Ast.Nodes,
Myc.Ast.Traverser,
Myc.Data.Value,
Myc.Data.Scalar;
type
// Dumps a bound AST into a human-readable format for debugging purposes.
TAstDumper = class(TAstTraverser)
private
FOutput: TStrings;
FIndent: Integer;
procedure Indent;
procedure Unindent;
procedure Log(const Text: string); overload;
procedure LogFmt(const Fmt: string; const Args: array of const); overload;
function FormatAddress(const Addr: TResolvedAddress): string;
protected
function Accept(const Node: IAstNode): TDataValue; override;
public
// Creates a new instance of the AST dumper.
constructor Create(const AOutput: TStrings);
// Traverses the given root node and writes the structural information into the output.
class procedure Dump(const RootNode: IAstNode; const Output: TStrings);
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 VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override;
function VisitFunctionCall(const Node: IFunctionCallNode): 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
{ TAstDumper }
class procedure TAstDumper.Dump(const RootNode: IAstNode; const Output: TStrings);
var
dumper: TAstDumper;
begin
if not Assigned(Output) then
exit;
Output.Clear;
dumper := TAstDumper.Create(Output);
try
dumper.Accept(RootNode);
finally
dumper.Free;
end;
end;
constructor TAstDumper.Create(const AOutput: TStrings);
begin
inherited Create;
FOutput := AOutput;
FIndent := 0;
end;
function TAstDumper.Accept(const Node: IAstNode): TDataValue;
begin
Result := TDataValue.Void;
if Assigned(Node) then
Result := inherited Accept(Node);
end;
procedure TAstDumper.Indent;
begin
inc(FIndent, 2);
end;
procedure TAstDumper.Unindent;
begin
dec(FIndent, 2);
end;
procedure TAstDumper.Log(const Text: string);
begin
FOutput.Add(StringOfChar(' ', FIndent) + Text);
end;
procedure TAstDumper.LogFmt(const Fmt: string; const Args: array of const);
begin
Log(Format(Fmt, Args));
end;
function TAstDumper.FormatAddress(const Addr: TResolvedAddress): string;
begin
case Addr.Kind of
akUnresolved: Result := '!! UNRESOLVED !!';
akLocalOrParent: Result := Format('LocalOrParent (Depth: %d, Slot: %d)', [Addr.ScopeDepth, Addr.SlotIndex]);
akUpvalue: Result := Format('Upvalue (Index: %d)', [Addr.SlotIndex]);
else
Result := 'Unknown Address Kind';
end;
end;
function TAstDumper.VisitConstant(const Node: IConstantNode): TDataValue;
begin
LogFmt('Constant: %s', [Node.Value.ToString]);
Result := TDataValue.Void;
end;
function TAstDumper.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
begin
LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(Node.Address)]);
Result := TDataValue.Void;
end;
function TAstDumper.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
begin
LogFmt('BinaryExpression: %s', [Node.Operator.ToString]);
Indent;
Accept(Node.Left);
Accept(Node.Right);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue;
begin
LogFmt('UnaryExpression: %s', [Node.Operator.ToString]);
Indent;
Accept(Node.Right);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitIfExpression(const Node: IIfExpressionNode): TDataValue;
begin
Log('IfExpression');
Indent;
Log('Condition:');
Accept(Node.Condition);
Log('Then:');
Accept(Node.ThenBranch);
if Assigned(Node.ElseBranch) then
begin
Log('Else:');
Accept(Node.ElseBranch);
end;
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
begin
Log('TernaryExpression');
Indent;
Log('Condition:');
Accept(Node.Condition);
Log('Then:');
Accept(Node.ThenBranch);
Log('Else:');
Accept(Node.ElseBranch);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
var
param: IIdentifierNode;
upvalueAddr: TResolvedAddress;
pair: TPair<string, Integer>;
begin
LogFmt('LambdaExpression (HasNested: %s)', [Node.HasNestedLambdas.ToString]);
Indent;
Log('Parameters:');
Indent;
for param in Node.Parameters do
Accept(param);
Unindent;
LogFmt('Upvalues (%d):', [Length(Node.Upvalues)]);
Indent;
for upvalueAddr in Node.Upvalues do
Log(FormatAddress(upvalueAddr));
Unindent;
Log('Scope Descriptor:');
Indent;
if Assigned(Node.ScopeDescriptor) then
for pair in Node.ScopeDescriptor.Symbols do
LogFmt('"%s" -> Slot %d', [pair.Key, pair.Value])
else
Log('(none)');
Unindent;
Log('Body:');
Accept(Node.Body);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
var
arg: IAstNode;
begin
LogFmt('FunctionCall (IsTailCall: %s)', [Node.IsTailCall.ToString]);
Indent;
Log('Callee:');
Accept(Node.Callee);
LogFmt('Arguments (%d):', [Length(Node.Arguments)]);
Indent;
for arg in Node.Arguments do
Accept(arg);
Unindent;
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
var
expr: IAstNode;
begin
Log('BlockExpression');
Indent;
for expr in Node.Expressions do
Accept(expr);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
begin
Log('VariableDeclaration');
Indent;
Accept(Node.Identifier);
if Assigned(Node.Initializer) then
begin
Log('Initializer:');
Accept(Node.Initializer);
end;
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitAssignment(const Node: IAssignmentNode): TDataValue;
begin
Log('Assignment');
Indent;
Accept(Node.Identifier);
Log('Value:');
Accept(Node.Value);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitIndexer(const Node: IIndexerNode): TDataValue;
begin
Log('Indexer');
Indent;
Log('Base:');
Accept(Node.Base);
Log('Index:');
Accept(Node.Index);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue;
begin
Log('MemberAccess');
Indent;
Log('Base:');
Accept(Node.Base);
Log('Member:');
Accept(Node.Member);
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
begin
LogFmt('CreateSeries: %s', [Node.Definition]);
Result := TDataValue.Void;
end;
function TAstDumper.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue;
begin
Log('AddSeriesItem');
Indent;
Log('Series:');
Accept(Node.Series);
Log('Value:');
Accept(Node.Value);
if Assigned(Node.Lookback) then
begin
Log('Lookback:');
Accept(Node.Lookback);
end;
Unindent;
Result := TDataValue.Void;
end;
function TAstDumper.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue;
begin
Log('SeriesLength');
Indent;
Log('Series:');
Accept(Node.Series);
Unindent;
Result := TDataValue.Void;
end;
end.