unit Myc.Ast.Printer; interface uses System.SysUtils, System.Classes, System.Generics.Collections, Myc.Data.Value, Myc.Ast.Nodes, Myc.Ast; type // A visitor that converts an AST into a LISP-like (Clojure-style) string representation. TPrettyPrintVisitor = class(TInterfacedObject, IAstVisitor) private FBuilder: TStringBuilder; FIndentLevel: Integer; procedure Indent; procedure Unindent; procedure Append(const S: string); procedure NewLine; public constructor Create; destructor Destroy; override; function GetResult: string; // IAstVisitor function Execute(const RootNode: IAstNode): TDataValue; function VisitConstant(const Node: IConstantNode): TDataValue; function VisitIdentifier(const Node: IIdentifierNode): TDataValue; function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; function VisitAssignment(const Node: IAssignmentNode): TDataValue; function VisitIndexer(const Node: IIndexerNode): TDataValue; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; function VisitRecurNode(const Node: IRecurNode): TDataValue; end; implementation uses Myc.Data.Scalar; { TPrettyPrintVisitor } constructor TPrettyPrintVisitor.Create; begin inherited Create; FBuilder := TStringBuilder.Create; FIndentLevel := 0; end; destructor TPrettyPrintVisitor.Destroy; begin FBuilder.Free; inherited Destroy; end; function TPrettyPrintVisitor.GetResult: string; begin Result := FBuilder.ToString; 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; function TPrettyPrintVisitor.Execute(const RootNode: IAstNode): TDataValue; begin if Assigned(RootNode) then RootNode.Accept(Self); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitConstant(const Node: IConstantNode): TDataValue; var val: TDataValue; begin val := Node.Value; if val.Kind = vkText then Append('"' + val.AsText + '"') else Append(val.ToString); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue; begin Append(Node.Name); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; begin Append('(' + Node.Operator.ToString); Append(' '); Node.Left.Accept(Self); Append(' '); Node.Right.Accept(Self); Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; begin Append('(' + Node.Operator.ToString); Append(' '); Node.Right.Accept(Self); Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitIfExpression(const Node: IIfExpressionNode): TDataValue; begin Append('(if '); Node.Condition.Accept(Self); Indent; NewLine; Node.ThenBranch.Accept(Self); if Assigned(Node.ElseBranch) then begin NewLine; Node.ElseBranch.Accept(Self); end; Unindent; NewLine; Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; begin Append('(if '); Node.Condition.Accept(Self); Indent; NewLine; Node.ThenBranch.Accept(Self); NewLine; Node.ElseBranch.Accept(Self); Unindent; NewLine; Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; var param: IIdentifierNode; sb: TStringBuilder; begin sb := TStringBuilder.Create; try for param in Node.Parameters do sb.Append(param.Name + ' '); if sb.Length > 0 then sb.Remove(sb.Length - 1, 1); Append('(fn [' + sb.ToString + ']'); finally sb.Free; end; Indent; NewLine; Node.Body.Accept(Self); Unindent; NewLine; Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; var arg: IAstNode; begin Append('('); Node.Callee.Accept(Self); Indent; for arg in Node.Arguments do begin NewLine; arg.Accept(Self); end; Unindent; if Length(Node.Arguments) > 0 then NewLine; Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitRecurNode(const Node: IRecurNode): TDataValue; var arg: IAstNode; begin Append('(recur'); Indent; for arg in Node.Arguments do begin NewLine; arg.Accept(Self); end; Unindent; if Length(Node.Arguments) > 0 then NewLine; Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; var expr: IAstNode; begin Append('(do'); Indent; for expr in Node.Expressions do begin NewLine; expr.Accept(Self); end; Unindent; NewLine; Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; begin Append('(def '); Node.Identifier.Accept(Self); if Assigned(Node.Initializer) then begin Append(' '); Node.Initializer.Accept(Self); end; Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitAssignment(const Node: IAssignmentNode): TDataValue; begin Append('(assign '); Node.Identifier.Accept(Self); Append(' '); Node.Value.Accept(Self); Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue; begin Append('(get '); Node.Base.Accept(Self); Append(' '); Node.Index.Accept(Self); Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; begin Append('(.'); Node.Member.Accept(Self); Append(' '); Node.Base.Accept(Self); Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; begin Append(Format('(new-series "%s")', [Node.Definition])); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; begin Append('(add-item '); Node.Series.Accept(Self); Append(' '); Node.Value.Accept(Self); if Assigned(Node.Lookback) then begin Append(' '); Node.Lookback.Accept(Self); end; Append(')'); Result := TDataValue.Void; end; function TPrettyPrintVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; begin Append('(count '); Node.Series.Accept(Self); Append(')'); Result := TDataValue.Void; end; end.