Files
MycLib/Src/AST/Myc.Ast.Script.Print.pas
T
Michael Schimmel 22674b962b Generic Visitors
2026-01-03 19:14:18 +01:00

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;
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.