328 lines
8.4 KiB
ObjectPascal
328 lines
8.4 KiB
ObjectPascal
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.
|