557 lines
15 KiB
ObjectPascal
557 lines
15 KiB
ObjectPascal
unit Myc.Ast.Dumper;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
System.Classes,
|
|
System.Generics.Collections,
|
|
Myc.Ast.Visitor,
|
|
Myc.Data.Value,
|
|
Myc.Data.Scalar,
|
|
Myc.Ast.Nodes,
|
|
Myc.Ast.Scope,
|
|
Myc.Ast;
|
|
|
|
type
|
|
// Dumps a bound AST into a human-readable format for debugging purposes.
|
|
IAstDumper = interface(IAstVisitor)
|
|
procedure Execute(const RootNode: IAstNode);
|
|
end;
|
|
|
|
TAstDumper = class(TAstVisitor, IAstDumper)
|
|
private
|
|
FOutput: TStrings;
|
|
FIndent: Integer;
|
|
procedure Indent;
|
|
procedure Unindent;
|
|
procedure Log(const Text: string; const Node: IAstNode = nil); overload;
|
|
procedure LogFmt(const Fmt: string; const Args: array of const; const Node: IAstNode = nil); overload;
|
|
function FormatAddress(const Addr: TResolvedAddress): string;
|
|
|
|
strict private
|
|
// Internal visit helpers with strict IAstNode signature
|
|
function VisitConstant(const Node: IAstNode): TVoid;
|
|
function VisitIdentifier(const Node: IAstNode): TVoid;
|
|
function VisitKeyword(const Node: IAstNode): TVoid;
|
|
function VisitIfExpression(const Node: IAstNode): TVoid;
|
|
function VisitCondExpression(const Node: IAstNode): TVoid;
|
|
function VisitLambdaExpression(const Node: IAstNode): TVoid;
|
|
function VisitFunctionCall(const Node: IAstNode): TVoid;
|
|
function VisitMacroExpansionNode(const Node: IAstNode): TVoid;
|
|
function VisitRecurNode(const Node: IAstNode): TVoid;
|
|
function VisitBlockExpression(const Node: IAstNode): TVoid;
|
|
function VisitVariableDeclaration(const Node: IAstNode): TVoid;
|
|
function VisitAssignment(const Node: IAstNode): TVoid;
|
|
function VisitMacroDefinition(const Node: IAstNode): TVoid;
|
|
function VisitQuasiquote(const Node: IAstNode): TVoid;
|
|
function VisitUnquote(const Node: IAstNode): TVoid;
|
|
function VisitUnquoteSplicing(const Node: IAstNode): TVoid;
|
|
function VisitIndexer(const Node: IAstNode): TVoid;
|
|
function VisitMemberAccess(const Node: IAstNode): TVoid;
|
|
function VisitRecordLiteral(const Node: IAstNode): TVoid;
|
|
function VisitRecordField(const Node: IAstNode): TVoid;
|
|
function VisitCreateSeries(const Node: IAstNode): TVoid;
|
|
function VisitAddSeriesItem(const Node: IAstNode): TVoid;
|
|
function VisitNop(const Node: IAstNode): TVoid;
|
|
function VisitTuple(const Node: IAstNode): TVoid;
|
|
function VisitPipe(const Node: IAstNode): TVoid;
|
|
|
|
protected
|
|
procedure SetupHandlers; override;
|
|
|
|
public
|
|
constructor Create(const AOutput: TStrings);
|
|
class procedure Dump(const RootNode: IAstNode; const Output: TStrings);
|
|
procedure Execute(const RootNode: IAstNode);
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Myc.Data.Keyword,
|
|
Myc.Ast.Types;
|
|
|
|
{ TAstDumper }
|
|
|
|
class procedure TAstDumper.Dump(const RootNode: IAstNode; const Output: TStrings);
|
|
var
|
|
dumper: TAstDumper;
|
|
begin
|
|
if (not Assigned(Output)) or (not Assigned(RootNode)) then
|
|
exit;
|
|
|
|
Output.Clear;
|
|
dumper := TAstDumper.Create(Output);
|
|
try
|
|
dumper.Execute(RootNode);
|
|
finally
|
|
dumper.Free;
|
|
end;
|
|
end;
|
|
|
|
constructor TAstDumper.Create(const AOutput: TStrings);
|
|
begin
|
|
inherited Create;
|
|
FOutput := AOutput;
|
|
FIndent := 0;
|
|
end;
|
|
|
|
procedure TAstDumper.SetupHandlers;
|
|
begin
|
|
Register(akConstant, VisitConstant);
|
|
Register(akIdentifier, VisitIdentifier);
|
|
Register(akKeyword, VisitKeyword);
|
|
Register(akTuple, VisitTuple);
|
|
Register(akRecordField, VisitRecordField);
|
|
Register(akIfExpression, VisitIfExpression);
|
|
Register(akCondExpression, VisitCondExpression);
|
|
Register(akLambdaExpression, VisitLambdaExpression);
|
|
Register(akFunctionCall, VisitFunctionCall);
|
|
Register(akMacroExpansion, VisitMacroExpansionNode);
|
|
Register(akBlockExpression, VisitBlockExpression);
|
|
Register(akVariableDeclaration, VisitVariableDeclaration);
|
|
Register(akAssignment, VisitAssignment);
|
|
Register(akMacroDefinition, VisitMacroDefinition);
|
|
Register(akQuasiquote, VisitQuasiquote);
|
|
Register(akUnquote, VisitUnquote);
|
|
Register(akUnquoteSplicing, VisitUnquoteSplicing);
|
|
Register(akIndexer, VisitIndexer);
|
|
Register(akMemberAccess, VisitMemberAccess);
|
|
Register(akRecordLiteral, VisitRecordLiteral);
|
|
Register(akCreateSeries, VisitCreateSeries);
|
|
Register(akAddSeriesItem, VisitAddSeriesItem);
|
|
Register(akRecur, VisitRecurNode);
|
|
Register(akNop, VisitNop);
|
|
Register(akPipe, VisitPipe);
|
|
end;
|
|
|
|
procedure TAstDumper.Execute(const RootNode: IAstNode);
|
|
begin
|
|
if Assigned(RootNode) then
|
|
Visit(RootNode);
|
|
end;
|
|
|
|
procedure TAstDumper.Indent;
|
|
begin
|
|
inc(FIndent, 2);
|
|
end;
|
|
|
|
procedure TAstDumper.Unindent;
|
|
begin
|
|
dec(FIndent, 2);
|
|
end;
|
|
|
|
procedure TAstDumper.Log(const Text: string; const Node: IAstNode);
|
|
var
|
|
staticType: IStaticType;
|
|
begin
|
|
var typeStr := '';
|
|
if Assigned(Node) and Node.IsTyped then
|
|
begin
|
|
staticType := Node.AsTypedNode.StaticType;
|
|
if Assigned(staticType) then
|
|
typeStr := Format(' <Type: %s>', [staticType.ToString])
|
|
else
|
|
typeStr := ' <Type: nil>';
|
|
end;
|
|
FOutput.Add(StringOfChar(' ', FIndent) + Text + typeStr);
|
|
end;
|
|
|
|
procedure TAstDumper.LogFmt(const Fmt: string; const Args: array of const; const Node: IAstNode);
|
|
begin
|
|
Log(Format(Fmt, Args), Node);
|
|
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: IAstNode): TVoid;
|
|
begin
|
|
LogFmt('Constant: %s', [Node.AsConstant.Value.ToString], Node);
|
|
end;
|
|
|
|
function TAstDumper.VisitIdentifier(const Node: IAstNode): TVoid;
|
|
var
|
|
I: IIdentifierNode;
|
|
begin
|
|
I := Node.AsIdentifier;
|
|
if I.Address.Kind <> akUnresolved then
|
|
LogFmt('Identifier: %s -> %s', [I.Name, FormatAddress(I.Address)], Node)
|
|
else
|
|
LogFmt('Identifier: %s (unbound)', [I.Name], Node);
|
|
end;
|
|
|
|
function TAstDumper.VisitKeyword(const Node: IAstNode): TVoid;
|
|
begin
|
|
LogFmt('Keyword: :%s', [Node.AsKeyword.Value.Name], Node);
|
|
end;
|
|
|
|
function TAstDumper.VisitIfExpression(const Node: IAstNode): TVoid;
|
|
var
|
|
E: IIfExpressionNode;
|
|
begin
|
|
E := Node.AsIfExpression;
|
|
Log('IfExpression', Node);
|
|
Indent;
|
|
Log('Condition:');
|
|
Visit(E.Condition);
|
|
Log('Then:');
|
|
Visit(E.ThenBranch);
|
|
if Assigned(E.ElseBranch) then
|
|
begin
|
|
Log('Else:');
|
|
Visit(E.ElseBranch);
|
|
end;
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitCondExpression(const Node: IAstNode): TVoid;
|
|
var
|
|
E: ICondExpressionNode;
|
|
i: Integer;
|
|
begin
|
|
E := Node.AsCondExpression;
|
|
LogFmt('CondExpression (%d pairs)', [Length(E.Pairs)], Node);
|
|
Indent;
|
|
for i := 0 to High(E.Pairs) do
|
|
begin
|
|
LogFmt('Pair %d:', [i]);
|
|
Indent;
|
|
Log('Condition:');
|
|
Visit(E.Pairs[i].Condition);
|
|
Log('Branch:');
|
|
Visit(E.Pairs[i].Branch);
|
|
Unindent;
|
|
end;
|
|
Log('Else:');
|
|
Visit(E.ElseBranch);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitLambdaExpression(const Node: IAstNode): TVoid;
|
|
var
|
|
E: ILambdaExpressionNode;
|
|
symbols: TArray<string>;
|
|
layout: IScopeLayout;
|
|
slot: Integer;
|
|
typ: IStaticType;
|
|
begin
|
|
E := Node.AsLambdaExpression;
|
|
LogFmt(
|
|
'LambdaExpression (HasNested: %s, IsPure: %s)',
|
|
[E.HasNestedLambdas.ToString(TUseBoolStrs.True), E.IsPure.ToString(TUseBoolStrs.True)],
|
|
Node
|
|
);
|
|
Indent;
|
|
|
|
if Assigned(E.Layout) then
|
|
begin
|
|
LogFmt('Scope: Layout Slots=%d', [E.Layout.SlotCount]);
|
|
if Assigned(E.Descriptor) then
|
|
begin
|
|
Log('Symbol Table:');
|
|
Indent;
|
|
layout := E.Layout;
|
|
symbols := layout.GetSymbols;
|
|
TArray.Sort<string>(symbols);
|
|
for var name in symbols do
|
|
begin
|
|
slot := layout.FindSlot(name);
|
|
typ := E.Descriptor.GetSymbolType(slot);
|
|
LogFmt('"%s" -> Slot %d (Type: %s)', [name, slot, typ.ToString]);
|
|
end;
|
|
Unindent;
|
|
end;
|
|
end;
|
|
|
|
Log('Parameters:');
|
|
Indent;
|
|
Visit(E.Parameters);
|
|
Unindent;
|
|
|
|
if Length(E.Upvalues) > 0 then
|
|
begin
|
|
LogFmt('Captured Upvalues (%d):', [Length(E.Upvalues)]);
|
|
Indent;
|
|
for var addr in E.Upvalues do
|
|
Log(FormatAddress(addr));
|
|
Unindent;
|
|
end;
|
|
|
|
Log('Body:');
|
|
Visit(E.Body);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitFunctionCall(const Node: IAstNode): TVoid;
|
|
var
|
|
C: IFunctionCallNode;
|
|
argTypes: TArray<string>;
|
|
i: Integer;
|
|
args: ITupleNode;
|
|
argsElements: TArray<IAstNode>;
|
|
begin
|
|
C := Node.AsFunctionCall;
|
|
args := C.Arguments;
|
|
argsElements := args.Elements;
|
|
|
|
LogFmt(
|
|
'FunctionCall (IsTailCall: %s, StaticTarget: %s, IsTargetPure: %s)',
|
|
[
|
|
C.IsTailCall.ToString(TUseBoolStrs.True),
|
|
Assigned(C.StaticTarget).ToString(TUseBoolStrs.True),
|
|
C.IsTargetPure.ToString(TUseBoolStrs.True)
|
|
],
|
|
Node
|
|
);
|
|
|
|
if Assigned(C.StaticTarget) then
|
|
begin
|
|
Indent;
|
|
SetLength(argTypes, Length(argsElements));
|
|
for i := 0 to High(argsElements) do
|
|
begin
|
|
if argsElements[i].IsTyped then
|
|
argTypes[i] := argsElements[i].AsTypedNode.StaticType.ToString
|
|
else
|
|
argTypes[i] := 'Untyped';
|
|
end;
|
|
LogFmt('ResolvedSig: Method(%s): %s', [string.Join(', ', argTypes), C.StaticType.ToString]);
|
|
Unindent;
|
|
end;
|
|
|
|
Indent;
|
|
Log('Callee:');
|
|
Visit(C.Callee);
|
|
LogFmt('Arguments (%d):', [Length(argsElements)]);
|
|
Visit(args);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitMacroExpansionNode(const Node: IAstNode): TVoid;
|
|
var
|
|
M: IMacroExpansionNode;
|
|
begin
|
|
M := Node.AsMacroExpansion;
|
|
Log('MacroExpansion', Node);
|
|
Indent;
|
|
Log('Original Call:');
|
|
Visit(M.CallNode);
|
|
Log('Expanded Body:');
|
|
Visit(M.ExpandedBody);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitRecurNode(const Node: IAstNode): TVoid;
|
|
begin
|
|
Log('Recur', Node);
|
|
Indent;
|
|
Visit(Node.AsRecur.Arguments);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitBlockExpression(const Node: IAstNode): TVoid;
|
|
begin
|
|
Log('BlockExpression', Node);
|
|
Indent;
|
|
Visit(Node.AsBlockExpression.Expressions);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitVariableDeclaration(const Node: IAstNode): TVoid;
|
|
var
|
|
V: IVariableDeclarationNode;
|
|
begin
|
|
V := Node.AsVariableDeclaration;
|
|
LogFmt('VariableDeclaration (IsBoxed: %s)', [V.IsBoxed.ToString(TUseBoolStrs.True)], Node);
|
|
Indent;
|
|
Visit(V.Target);
|
|
if Assigned(V.Initializer) then
|
|
begin
|
|
Log('Initializer:');
|
|
Visit(V.Initializer);
|
|
end;
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitAssignment(const Node: IAstNode): TVoid;
|
|
var
|
|
A: IAssignmentNode;
|
|
begin
|
|
A := Node.AsAssignment;
|
|
Log('Assignment', Node);
|
|
Indent;
|
|
Visit(A.Target);
|
|
Log('Value:');
|
|
Visit(A.Value);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitMacroDefinition(const Node: IAstNode): TVoid;
|
|
var
|
|
M: IMacroDefinitionNode;
|
|
begin
|
|
M := Node.AsMacroDefinition;
|
|
Log('MacroDefinition', Node);
|
|
Indent;
|
|
Log('Name:');
|
|
Visit(M.Name);
|
|
Log('Parameters:');
|
|
Visit(M.Parameters);
|
|
Log('Body:');
|
|
Visit(M.Body);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitQuasiquote(const Node: IAstNode): TVoid;
|
|
begin
|
|
Log('Quasiquote', Node);
|
|
Indent;
|
|
Visit(Node.AsQuasiquote.Expression);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitUnquote(const Node: IAstNode): TVoid;
|
|
begin
|
|
Log('Unquote', Node);
|
|
Indent;
|
|
Visit(Node.AsUnquote.Expression);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitUnquoteSplicing(const Node: IAstNode): TVoid;
|
|
begin
|
|
Log('UnquoteSplicing', Node);
|
|
Indent;
|
|
Visit(Node.AsUnquoteSplicing.Expression);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitIndexer(const Node: IAstNode): TVoid;
|
|
var
|
|
I: IIndexerNode;
|
|
begin
|
|
I := Node.AsIndexer;
|
|
Log('Indexer', Node);
|
|
Indent;
|
|
Log('Base:');
|
|
Visit(I.Base);
|
|
Log('Index:');
|
|
Visit(I.Index);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitMemberAccess(const Node: IAstNode): TVoid;
|
|
var
|
|
M: IMemberAccessNode;
|
|
begin
|
|
M := Node.AsMemberAccess;
|
|
Log('MemberAccess', Node);
|
|
Indent;
|
|
Log('Base:');
|
|
Visit(M.Base);
|
|
Log('Member:');
|
|
Visit(M.Member);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitRecordLiteral(const Node: IAstNode): TVoid;
|
|
var
|
|
R: IRecordLiteralNode;
|
|
begin
|
|
R := Node.AsRecordLiteral;
|
|
LogFmt('RecordLiteral (%d fields)', [Length(R.Fields.Elements)], Node);
|
|
Indent;
|
|
Visit(R.Fields);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitRecordField(const Node: IAstNode): TVoid;
|
|
var
|
|
F: IRecordFieldNode;
|
|
begin
|
|
F := Node.AsRecordField;
|
|
LogFmt('Field :%s', [F.Key.Value.Name]);
|
|
Indent;
|
|
Visit(F.Value);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitCreateSeries(const Node: IAstNode): TVoid;
|
|
begin
|
|
Log('CreateSeries', Node);
|
|
Indent;
|
|
Log('Definition:');
|
|
Visit(Node.AsCreateSeries.DefinitionNode);
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitAddSeriesItem(const Node: IAstNode): TVoid;
|
|
var
|
|
A: IAddSeriesItemNode;
|
|
begin
|
|
A := Node.AsAddSeriesItem;
|
|
Log('AddSeriesItem', Node);
|
|
Indent;
|
|
Log('Series:');
|
|
Visit(A.Series);
|
|
Log('Value:');
|
|
Visit(A.Value);
|
|
if Assigned(A.Lookback) then
|
|
begin
|
|
Log('Lookback:');
|
|
Visit(A.Lookback);
|
|
end;
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitNop(const Node: IAstNode): TVoid;
|
|
begin
|
|
Log('Nop', Node);
|
|
end;
|
|
|
|
function TAstDumper.VisitTuple(const Node: IAstNode): TVoid;
|
|
var
|
|
T: ITupleNode;
|
|
i: Integer;
|
|
elements: TArray<IAstNode>;
|
|
begin
|
|
T := Node.AsTuple;
|
|
elements := T.Elements;
|
|
LogFmt('Tuple (%d elements)', [Length(elements)], Node);
|
|
Indent;
|
|
for i := 0 to High(elements) do
|
|
begin
|
|
LogFmt('Item %d:', [i]);
|
|
Indent;
|
|
Visit(elements[i]);
|
|
Unindent;
|
|
end;
|
|
Unindent;
|
|
end;
|
|
|
|
function TAstDumper.VisitPipe(const Node: IAstNode): TVoid;
|
|
var
|
|
P: IPipeNode;
|
|
begin
|
|
P := Node.AsPipe;
|
|
Log('Pipe', Node);
|
|
Indent;
|
|
Log('Inputs (Tuple of Tuples):');
|
|
Visit(P.Inputs);
|
|
Log('Transformation:');
|
|
Visit(P.Transformation);
|
|
Unindent;
|
|
end;
|
|
|
|
end.
|