unit Myc.Ast.Dumper; interface uses System.SysUtils, System.Classes, System.Generics.Collections, Myc.Ast.Nodes, Myc.Ast.Visitor, Myc.Data.Value, Myc.Data.Scalar, Myc.Ast; type // Dumps a bound AST into a human-readable format for debugging purposes. // It inherits from the abstract TAstVisitor to implement the IAstVisitor interface. IAstDumper = interface(IAstVisitor) procedure Execute(const RootNode: IAstNode); end; // Inherits from the new non-generic TAstVisitor base class TAstDumper = class(TAstVisitor, IAstDumper) 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 // Override abstract procedures from TAstVisitor procedure VisitConstant(const Node: IConstantNode); override; procedure VisitIdentifier(const Node: IIdentifierNode); override; procedure VisitKeyword(const Node: IKeywordNode); override; procedure VisitBinaryExpression(const Node: IBinaryExpressionNode); override; procedure VisitUnaryExpression(const Node: IUnaryExpressionNode); override; procedure VisitIfExpression(const Node: IIfExpressionNode); override; procedure VisitTernaryExpression(const Node: ITernaryExpressionNode); override; procedure VisitLambdaExpression(const Node: ILambdaExpressionNode); override; procedure VisitFunctionCall(const Node: IFunctionCallNode); override; procedure VisitMacroExpansionNode(const Node: IMacroExpansionNode); override; procedure VisitRecurNode(const Node: IRecurNode); override; procedure VisitBlockExpression(const Node: IBlockExpressionNode); override; procedure VisitVariableDeclaration(const Node: IVariableDeclarationNode); override; procedure VisitAssignment(const Node: IAssignmentNode); override; procedure VisitMacroDefinition(const Node: IMacroDefinitionNode); override; procedure VisitQuasiquote(const Node: IQuasiquoteNode); override; procedure VisitUnquote(const Node: IUnquoteNode); override; procedure VisitUnquoteSplicing(const Node: IUnquoteSplicingNode); override; procedure VisitIndexer(const Node: IIndexerNode); override; procedure VisitMemberAccess(const Node: IMemberAccessNode); override; procedure VisitRecordLiteral(const Node: IRecordLiteralNode); override; procedure VisitCreateSeries(const Node: ICreateSeriesNode); override; procedure VisitAddSeriesItem(const Node: IAddSeriesItemNode); override; procedure VisitSeriesLength(const Node: ISeriesLengthNode); override; procedure VisitNop(const Node: INopNode); 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); procedure Execute(const RootNode: IAstNode); end; implementation uses Myc.Data.Keyword; // Myc.Ast.Binding.Nodes; // Removed { 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.Execute(const RootNode: IAstNode); begin if Assigned(RootNode) then RootNode.Accept(Self); 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; procedure TAstDumper.VisitConstant(const Node: IConstantNode); begin LogFmt('Constant: %s', [Node.Value.ToString]); end; procedure TAstDumper.VisitIdentifier(const Node: IIdentifierNode); var adr: TResolvedAddress; begin adr := Node.AsIdentifier.Address; if adr.Kind <> akUnresolved then LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(adr)]) else LogFmt('Identifier: %s (unbound)', [Node.Name]); end; procedure TAstDumper.VisitKeyword(const Node: IKeywordNode); begin LogFmt('Keyword: :%s', [Node.Value.Name]); end; procedure TAstDumper.VisitBinaryExpression(const Node: IBinaryExpressionNode); begin LogFmt('BinaryExpression: %s', [Node.Operator.ToString]); Indent; Node.Left.Accept(Self); Node.Right.Accept(Self); Unindent; end; procedure TAstDumper.VisitUnaryExpression(const Node: IUnaryExpressionNode); begin LogFmt('UnaryExpression: %s', [Node.Operator.ToString]); Indent; Node.Right.Accept(Self); Unindent; end; procedure TAstDumper.VisitIfExpression(const Node: IIfExpressionNode); begin Log('IfExpression'); Indent; Log('Condition:'); Node.Condition.Accept(Self); Log('Then:'); Node.ThenBranch.Accept(Self); if Assigned(Node.ElseBranch) then begin Log('Else:'); Node.ElseBranch.Accept(Self); end; Unindent; end; procedure TAstDumper.VisitTernaryExpression(const Node: ITernaryExpressionNode); begin Log('TernaryExpression'); Indent; Log('Condition:'); Node.Condition.Accept(Self); Log('Then:'); Node.ThenBranch.Accept(Self); Log('Else:'); Node.ElseBranch.Accept(Self); Unindent; end; procedure TAstDumper.VisitLambdaExpression(const Node: ILambdaExpressionNode); var upvalueAddr: TResolvedAddress; pair: TPair; boundNode: TLambdaExpressionNode; begin boundNode := Node as TLambdaExpressionNode; if Assigned(boundNode.ScopeDescriptor) then begin LogFmt('LambdaExpression (HasNested: %s)', [boundNode.HasNestedLambdas.ToString(TUseBoolStrs.True)]); Indent; Log('Parameters:'); Indent; for var param in boundNode.Parameters do param.Accept(Self); Unindent; LogFmt('Upvalues (%d):', [Length(boundNode.Upvalues)]); Indent; for upvalueAddr in boundNode.Upvalues do Log(FormatAddress(upvalueAddr)); Unindent; Log('Scope Descriptor:'); Indent; if Assigned(boundNode.ScopeDescriptor) then for pair in boundNode.ScopeDescriptor.Symbols do LogFmt('"%s" -> Slot %d', [pair.Key, pair.Value]) else Log('(none)'); Unindent; Log('Body:'); boundNode.Body.Accept(Self); Unindent; end else begin Log('LambdaExpression (unbound)'); Indent; Log('Parameters:'); Indent; for var param in Node.Parameters do param.Accept(Self); Unindent; Log('Body:'); Node.Body.Accept(Self); Unindent; end; end; procedure TAstDumper.VisitFunctionCall(const Node: IFunctionCallNode); var arg: IAstNode; N: TFunctionCallNode; begin N := (Node as TFunctionCallNode); LogFmt('FunctionCall (IsTailCall: %s)', [N.IsTailCall.ToString(TUseBoolStrs.True)]); Indent; Log('Callee:'); N.Callee.Accept(Self); LogFmt('Arguments (%d):', [Length(N.Arguments)]); Indent; for arg in N.Arguments do arg.Accept(Self); Unindent; Unindent; end; procedure TAstDumper.VisitMacroExpansionNode(const Node: IMacroExpansionNode); var arg: IAstNode; begin Log('MacroExpansion'); Indent; Log('Original Call:'); Indent; Log('Callee:'); Node.CallNode.Callee.Accept(Self); LogFmt('Arguments (%d):', [Length(Node.CallNode.Arguments)]); Indent; for arg in Node.CallNode.Arguments do arg.Accept(Self); Unindent; Unindent; Log('Expanded Body:'); Indent; Node.ExpandedBody.Accept(Self); Unindent; Unindent; end; procedure TAstDumper.VisitRecurNode(const Node: IRecurNode); var arg: IAstNode; begin // 'recur' must be a tail call, which is enforced by the binder. Log('Recur'); Indent; LogFmt('Arguments (%d):', [Length(Node.Arguments)]); Indent; for arg in Node.Arguments do arg.Accept(Self); Unindent; Unindent; end; procedure TAstDumper.VisitBlockExpression(const Node: IBlockExpressionNode); var expr: IAstNode; begin Log('BlockExpression'); Indent; for expr in Node.Expressions do expr.Accept(Self); Unindent; end; procedure TAstDumper.VisitVariableDeclaration(const Node: IVariableDeclarationNode); var N: TVariableDeclarationNode; begin N := (Node as TVariableDeclarationNode); LogFmt('VariableDeclaration (IsBoxed: %s)', [N.IsBoxed.ToString(TUseBoolStrs.True)]); Indent; N.Identifier.Accept(Self); if Assigned(N.Initializer) then begin Log('Initializer:'); N.Initializer.Accept(Self); end; Unindent; end; procedure TAstDumper.VisitAssignment(const Node: IAssignmentNode); begin Log('Assignment'); Indent; Node.Identifier.Accept(Self); Log('Value:'); Node.Value.Accept(Self); Unindent; end; procedure TAstDumper.VisitMacroDefinition(const Node: IMacroDefinitionNode); begin Log('MacroDefinition'); Indent; Log('Name:'); Indent; Node.Name.Accept(Self); Unindent; Log('Parameters:'); Indent; for var param in Node.Parameters do param.Accept(Self); Unindent; Log('Body:'); Indent; Node.Body.Accept(Self); Unindent; Unindent; end; procedure TAstDumper.VisitQuasiquote(const Node: IQuasiquoteNode); begin Log('Quasiquote'); Indent; Node.Expression.Accept(Self); Unindent; end; procedure TAstDumper.VisitUnquote(const Node: IUnquoteNode); begin Log('Unquote'); Indent; Node.Expression.Accept(Self); Unindent; end; procedure TAstDumper.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode); begin Log('UnquoteSplicing'); Indent; Node.Expression.Accept(Self); Unindent; end; procedure TAstDumper.VisitIndexer(const Node: IIndexerNode); begin Log('Indexer'); Indent; Log('Base:'); Node.Base.Accept(Self); Log('Index:'); Node.Index.Accept(Self); Unindent; end; procedure TAstDumper.VisitMemberAccess(const Node: IMemberAccessNode); begin Log('MemberAccess'); Indent; Log('Base:'); Node.Base.Accept(Self); Log('Member:'); Node.Member.Accept(Self); Unindent; end; procedure TAstDumper.VisitRecordLiteral(const Node: IRecordLiteralNode); var field: TRecordFieldLiteral; begin LogFmt('RecordLiteral (%d fields)', [Length(Node.Fields)]); Indent; for field in Node.Fields do begin LogFmt('Field :%s', [field.Key.Value.Name]); Indent; field.Value.Accept(Self); Unindent; end; Unindent; end; procedure TAstDumper.VisitCreateSeries(const Node: ICreateSeriesNode); begin LogFmt('CreateSeries: %s', [Node.Definition]); end; procedure TAstDumper.VisitAddSeriesItem(const Node: IAddSeriesItemNode); begin Log('AddSeriesItem'); Indent; Log('Series:'); Node.Series.Accept(Self); Log('Value:'); Node.Value.Accept(Self); if Assigned(Node.Lookback) then begin Log('Lookback:'); Node.Lookback.Accept(Self); end; Unindent; end; procedure TAstDumper.VisitNop(const Node: INopNode); begin Log('Nop'); end; procedure TAstDumper.VisitSeriesLength(const Node: ISeriesLengthNode); begin Log('SeriesLength'); Indent; Log('Series:'); Node.Series.Accept(Self); Unindent; end; end.