538 lines
13 KiB
ObjectPascal
538 lines
13 KiB
ObjectPascal
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;
|
|
|
|
// Tuple handles generic lists [ ... ]
|
|
function VisitTuple(const N: IAstNode): TVoid;
|
|
|
|
// Single Element
|
|
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(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(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 ---
|
|
|
|
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.VisitTuple(const N: IAstNode): TVoid;
|
|
var
|
|
T: ITupleNode;
|
|
i: Integer;
|
|
begin
|
|
T := N.AsTuple;
|
|
Append('[');
|
|
for i := 0 to T.Count - 1 do
|
|
begin
|
|
if i > 0 then
|
|
Append(' ');
|
|
Visit(T.Items[i]);
|
|
end;
|
|
Append(']');
|
|
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 ');
|
|
// Parameters is ITupleNode -> prints [ ... ] automatically
|
|
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(' ');
|
|
// Parameters is ITupleNode -> prints [ ... ] automatically
|
|
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;
|
|
i: Integer;
|
|
args: ITupleNode;
|
|
begin
|
|
C := N.AsFunctionCall;
|
|
args := C.Arguments;
|
|
|
|
// Special case for 'quote'
|
|
if (C.Callee.Kind = akIdentifier) and (C.Callee.AsIdentifier.Name = 'quote') and (args.Count = 1) then
|
|
begin
|
|
Append('''');
|
|
Visit(args.Items[0]);
|
|
exit;
|
|
end;
|
|
|
|
Append('(');
|
|
Visit(C.Callee);
|
|
// Manually iterate arguments tuple to avoid printing [...] inside ()
|
|
for i := 0 to args.Count - 1 do
|
|
begin
|
|
Append(' ');
|
|
Visit(args.Items[i]);
|
|
end;
|
|
Append(')');
|
|
end;
|
|
|
|
function TPrettyPrintVisitor.VisitMacroExpansionNode(const N: IAstNode): TVoid;
|
|
begin
|
|
Visit(N.AsMacroExpansion.CallNode);
|
|
end;
|
|
|
|
function TPrettyPrintVisitor.VisitRecurNode(const N: IAstNode): TVoid;
|
|
var
|
|
R: IRecurNode;
|
|
i: Integer;
|
|
args: ITupleNode;
|
|
begin
|
|
R := N.AsRecur;
|
|
args := R.Arguments;
|
|
Append('(recur');
|
|
// Manually iterate arguments
|
|
for i := 0 to args.Count - 1 do
|
|
begin
|
|
Append(' ');
|
|
Visit(args.Items[i]);
|
|
end;
|
|
Append(')');
|
|
end;
|
|
|
|
function TPrettyPrintVisitor.VisitBlockExpression(const N: IAstNode): TVoid;
|
|
var
|
|
B: IBlockExpressionNode;
|
|
exprs: ITupleNode;
|
|
i: Integer;
|
|
begin
|
|
B := N.AsBlockExpression;
|
|
exprs := B.Expressions;
|
|
Append('(do');
|
|
Indent;
|
|
// Manually iterate expressions tuple for newlines
|
|
for i := 0 to exprs.Count - 1 do
|
|
begin
|
|
NewLine;
|
|
Visit(exprs.Items[i]);
|
|
end;
|
|
Unindent;
|
|
NewLine;
|
|
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;
|
|
fields: ITupleNode;
|
|
i: Integer;
|
|
begin
|
|
R := N.AsRecordLiteral;
|
|
fields := R.Fields;
|
|
if fields.Count = 0 then
|
|
begin
|
|
Append('{}');
|
|
exit;
|
|
end;
|
|
Append('{');
|
|
Indent;
|
|
for i := 0 to fields.Count - 1 do
|
|
begin
|
|
NewLine;
|
|
Visit(fields.Items[i]);
|
|
end;
|
|
Unindent;
|
|
NewLine;
|
|
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.
|