Generic Visitors
This commit is contained in:
@@ -0,0 +1,537 @@
|
||||
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.
|
||||
Reference in New Issue
Block a user