unit Myc.Ast.Script.Print; interface uses System.SysUtils, System.Classes, Myc.Data.Value, Myc.Data.Scalar, Myc.Ast.Nodes, Myc.Ast.Visitor, Myc.Ast; type TPrettyPrintVisitor = class(TAstVisitor) strict private FBuilder: TStringBuilder; FIndentLevel: Integer; procedure Indent; procedure Unindent; procedure Append(const S: string); procedure NewLine; // Handlers now strictly accept IAstNode function VisitConstant(const N: IAstNode): TVoid; function VisitIdentifier(const N: IAstNode): TVoid; function VisitKeyword(const N: IAstNode): TVoid; function VisitParameterList(const N: IAstNode): TVoid; function VisitArgumentList(const N: IAstNode): TVoid; function VisitExpressionList(const N: IAstNode): TVoid; function VisitRecordFieldList(const N: IAstNode): TVoid; function VisitRecordField(const N: IAstNode): TVoid; function VisitIfExpression(const N: IAstNode): TVoid; function VisitCondExpression(const N: IAstNode): TVoid; function VisitLambdaExpression(const N: IAstNode): TVoid; function VisitMacroDefinition(const N: IAstNode): TVoid; function VisitQuasiquote(const N: IAstNode): TVoid; function VisitUnquote(const N: IAstNode): TVoid; function VisitUnquoteSplicing(const N: IAstNode): TVoid; function VisitFunctionCall(const N: IAstNode): TVoid; function VisitMacroExpansionNode(const N: IAstNode): TVoid; function VisitBlockExpression(const N: IAstNode): TVoid; function VisitVariableDeclaration(const N: IAstNode): TVoid; function VisitAssignment(const N: IAstNode): TVoid; function VisitIndexer(const N: IAstNode): TVoid; function VisitMemberAccess(const N: IAstNode): TVoid; function VisitRecordLiteral(const N: IAstNode): TVoid; function VisitCreateSeries(const N: IAstNode): TVoid; function VisitAddSeriesItem(const N: IAstNode): TVoid; function VisitSeriesLength(const N: IAstNode): TVoid; function VisitRecurNode(const N: IAstNode): TVoid; function VisitNop(const N: IAstNode): TVoid; function VisitPipeInput(const N: IAstNode): TVoid; function VisitPipeSelectorList(const N: IAstNode): TVoid; function VisitPipeInputList(const N: IAstNode): TVoid; function VisitPipe(const N: IAstNode): TVoid; protected procedure SetupHandlers; override; public constructor Create; destructor Destroy; override; function GetResult: string; procedure Execute(const RootNode: IAstNode); end; implementation { TPrettyPrintVisitor } constructor TPrettyPrintVisitor.Create; begin inherited Create; FBuilder := TStringBuilder.Create; FIndentLevel := 0; end; destructor TPrettyPrintVisitor.Destroy; begin FBuilder.Free; inherited; end; procedure TPrettyPrintVisitor.SetupHandlers; begin Register(akConstant, VisitConstant); Register(akIdentifier, VisitIdentifier); Register(akKeyword, VisitKeyword); Register(akParameterList, VisitParameterList); Register(akArgumentList, VisitArgumentList); Register(akExpressionList, VisitExpressionList); Register(akRecordFieldList, VisitRecordFieldList); 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(akSeriesLength, VisitSeriesLength); Register(akRecur, VisitRecurNode); Register(akNop, VisitNop); Register(akPipeInput, VisitPipeInput); Register(akPipeSelectorList, VisitPipeSelectorList); Register(akPipeInputList, VisitPipeInputList); Register(akPipe, VisitPipe); end; function TPrettyPrintVisitor.GetResult: string; begin Result := FBuilder.ToString; end; procedure TPrettyPrintVisitor.Execute(const RootNode: IAstNode); begin if Assigned(RootNode) then Visit(RootNode); end; procedure TPrettyPrintVisitor.Indent; begin Inc(FIndentLevel, 2); end; procedure TPrettyPrintVisitor.Unindent; begin Dec(FIndentLevel, 2); end; procedure TPrettyPrintVisitor.Append(const S: string); begin FBuilder.Append(S); end; procedure TPrettyPrintVisitor.NewLine; begin FBuilder.AppendLine; FBuilder.Append(''.PadLeft(FIndentLevel)); end; // --- Implementations (Clean & Typed) --- function TPrettyPrintVisitor.VisitPipeInput(const N: IAstNode): TVoid; var P: IPipeInputNode; begin P := N.AsPipeInput; Visit(P.StreamSource); Append(' '); Visit(P.Selectors); end; function TPrettyPrintVisitor.VisitPipeSelectorList(const N: IAstNode): TVoid; var L: IPipeSelectorList; i: Integer; begin L := N.AsPipeSelectorList; Append('['); for i := 0 to L.Count - 1 do begin if i > 0 then Append(' '); Visit(L[i]); end; Append(']'); end; function TPrettyPrintVisitor.VisitPipeInputList(const N: IAstNode): TVoid; var L: IPipeInputList; i: Integer; begin L := N.AsPipeInputList; Append('['); for i := 0 to L.Count - 1 do begin if i > 0 then Append(' '); Visit(L[i]); end; Append(']'); end; function TPrettyPrintVisitor.VisitPipe(const N: IAstNode): TVoid; var P: IPipeNode; begin P := N.AsPipe; Append('(pipe '); Visit(P.Inputs); Indent; NewLine; Visit(P.Transformation); Unindent; NewLine; Append(')'); end; function TPrettyPrintVisitor.VisitConstant(const N: IAstNode): TVoid; var C: IConstantNode; begin C := N.AsConstant; if C.Value.Kind = vkText then Append('"' + C.Value.AsText + '"') else Append(C.Value.ToString); end; function TPrettyPrintVisitor.VisitIdentifier(const N: IAstNode): TVoid; begin Append(N.AsIdentifier.Name); end; function TPrettyPrintVisitor.VisitKeyword(const N: IAstNode): TVoid; begin Append(':' + N.AsKeyword.Value.Name); end; function TPrettyPrintVisitor.VisitNop(const N: IAstNode): TVoid; begin Append('...'); end; function TPrettyPrintVisitor.VisitParameterList(const N: IAstNode): TVoid; var L: IParameterList; i: Integer; begin L := N.AsParameterList; Append('['); for i := 0 to L.Count - 1 do begin if i > 0 then Append(' '); Visit(L[i]); end; Append(']'); end; function TPrettyPrintVisitor.VisitArgumentList(const N: IAstNode): TVoid; var L: IArgumentList; i: Integer; begin L := N.AsArgumentList; for i := 0 to L.Count - 1 do begin Append(' '); Visit(L[i]); end; end; function TPrettyPrintVisitor.VisitExpressionList(const N: IAstNode): TVoid; var L: IExpressionList; item: IAstNode; begin L := N.AsExpressionList; Indent; for item in L do begin NewLine; Visit(item); end; Unindent; NewLine; end; function TPrettyPrintVisitor.VisitRecordFieldList(const N: IAstNode): TVoid; var L: IRecordFieldList; item: IRecordFieldNode; begin L := N.AsRecordFieldList; Indent; for item in L do begin NewLine; Visit(item); end; Unindent; NewLine; end; function TPrettyPrintVisitor.VisitRecordField(const N: IAstNode): TVoid; var F: IRecordFieldNode; begin F := N.AsRecordField; Visit(F.Key); Append(' '); Visit(F.Value); end; function TPrettyPrintVisitor.VisitIfExpression(const N: IAstNode): TVoid; var E: IIfExpressionNode; begin E := N.AsIfExpression; Append('(if '); Visit(E.Condition); Indent; NewLine; Visit(E.ThenBranch); if Assigned(E.ElseBranch) then begin NewLine; Visit(E.ElseBranch); end; Unindent; NewLine; Append(')'); end; function TPrettyPrintVisitor.VisitCondExpression(const N: IAstNode): TVoid; var E: ICondExpressionNode; begin E := N.AsCondExpression; Append('(?'); Indent; for var pair in E.Pairs do begin NewLine; Visit(pair.Condition); Append(' '); Visit(pair.Branch); end; NewLine; Visit(E.ElseBranch); Unindent; NewLine; Append(')'); end; function TPrettyPrintVisitor.VisitLambdaExpression(const N: IAstNode): TVoid; var E: ILambdaExpressionNode; begin E := N.AsLambdaExpression; Append('(fn '); Visit(E.Parameters); Indent; NewLine; Visit(E.Body); Unindent; NewLine; Append(')'); end; function TPrettyPrintVisitor.VisitMacroDefinition(const N: IAstNode): TVoid; var M: IMacroDefinitionNode; begin M := N.AsMacroDefinition; Append('(defmacro '); Visit(M.Name); Append(' '); Visit(M.Parameters); Indent; NewLine; Visit(M.Body); Unindent; NewLine; Append(')'); end; function TPrettyPrintVisitor.VisitQuasiquote(const N: IAstNode): TVoid; begin Append('`'); Visit(N.AsQuasiquote.Expression); end; function TPrettyPrintVisitor.VisitUnquote(const N: IAstNode): TVoid; begin Append('~'); Visit(N.AsUnquote.Expression); end; function TPrettyPrintVisitor.VisitUnquoteSplicing(const N: IAstNode): TVoid; begin Append('~@'); Visit(N.AsUnquoteSplicing.Expression); end; function TPrettyPrintVisitor.VisitFunctionCall(const N: IAstNode): TVoid; var C: IFunctionCallNode; begin C := N.AsFunctionCall; if (C.Callee.Kind = akIdentifier) and (C.Callee.AsIdentifier.Name = 'quote') and (C.Arguments.Count = 1) then begin Append(''''); Visit(C.Arguments[0]); exit; end; Append('('); Visit(C.Callee); Visit(C.Arguments); Append(')'); end; function TPrettyPrintVisitor.VisitMacroExpansionNode(const N: IAstNode): TVoid; begin Visit(N.AsMacroExpansion.CallNode); end; function TPrettyPrintVisitor.VisitRecurNode(const N: IAstNode): TVoid; begin Append('(recur'); Visit(N.AsRecur.Arguments); Append(')'); end; function TPrettyPrintVisitor.VisitBlockExpression(const N: IAstNode): TVoid; begin Append('(do'); Visit(N.AsBlockExpression.Expressions); Append(')'); end; function TPrettyPrintVisitor.VisitVariableDeclaration(const N: IAstNode): TVoid; var V: IVariableDeclarationNode; begin V := N.AsVariableDeclaration; Append('(def '); Visit(V.Target); if Assigned(V.Initializer) then begin Append(' '); Visit(V.Initializer); end; Append(')'); end; function TPrettyPrintVisitor.VisitAssignment(const N: IAstNode): TVoid; var A: IAssignmentNode; begin A := N.AsAssignment; Append('(assign '); Visit(A.Target); Append(' '); Visit(A.Value); Append(')'); end; function TPrettyPrintVisitor.VisitIndexer(const N: IAstNode): TVoid; var I: IIndexerNode; begin I := N.AsIndexer; Append('(get '); Visit(I.Base); Append(' '); Visit(I.Index); Append(')'); end; function TPrettyPrintVisitor.VisitMemberAccess(const N: IAstNode): TVoid; var M: IMemberAccessNode; begin M := N.AsMemberAccess; Append('(.'); Visit(M.Member); Append(' '); Visit(M.Base); Append(')'); end; function TPrettyPrintVisitor.VisitRecordLiteral(const N: IAstNode): TVoid; var R: IRecordLiteralNode; begin R := N.AsRecordLiteral; if R.Fields.Count = 0 then begin Append('{}'); exit; end; Append('{'); Visit(R.Fields); Append('}'); end; function TPrettyPrintVisitor.VisitCreateSeries(const N: IAstNode): TVoid; begin Append(Format('(new-series "%s")', [N.AsCreateSeries.Definition])); end; function TPrettyPrintVisitor.VisitAddSeriesItem(const N: IAstNode): TVoid; var A: IAddSeriesItemNode; begin A := N.AsAddSeriesItem; Append('(add-item '); Visit(A.Series); Append(' '); Visit(A.Value); if Assigned(A.Lookback) then begin Append(' '); Visit(A.Lookback); end; Append(')'); end; function TPrettyPrintVisitor.VisitSeriesLength(const N: IAstNode): TVoid; begin Append('(count '); Visit(N.AsSeriesLength.Series); Append(')'); end; end.