Files
MycLib/ASTPlayground/Myc.Fmx.AstEditor.Text.pas
T
2025-11-05 12:19:34 +01:00

280 lines
9.1 KiB
ObjectPascal

unit Myc.Fmx.AstEditor.Text;
interface
uses
System.SysUtils,
Myc.Data.Value,
Myc.Data.Scalar,
Myc.Ast.Visitor,
Myc.Ast.Nodes;
type
// This visitor converts an AST expression subtree into a single string.
// It inherits from the generic visitor, returning a 'string' for each node.
TAstToTextVisitor = class(TAstVisitor<string>)
protected
function VisitConstant(const Node: IConstantNode): string; override;
function VisitIdentifier(const Node: IIdentifierNode): string; override;
function VisitKeyword(const Node: IKeywordNode): string; override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): string; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): string; override;
function VisitIfExpression(const Node: IIfExpressionNode): string; override;
function VisitTernaryExpression(const Node: ITernaryExpressionNode): string; override;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): string; override;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): string; override;
function VisitFunctionCall(const Node: IFunctionCallNode): string; override;
function VisitMacroExpansionNode(const Node: IMacroExpansionNode): string; override;
function VisitBlockExpression(const Node: IBlockExpressionNode): string; override;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): string; override;
function VisitAssignment(const Node: IAssignmentNode): string; override;
function VisitIndexer(const Node: IIndexerNode): string; override;
function VisitMemberAccess(const Node: IMemberAccessNode): string; override;
function VisitQuasiquote(const Node: IQuasiquoteNode): string; override;
function VisitUnquote(const Node: IUnquoteNode): string; override;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): string; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): string; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): string; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): string; override;
function VisitRecurNode(const Node: IRecurNode): string; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): string; override;
end;
implementation
uses
System.Classes, // For TStringBuilder
Myc.Data.Keyword;
{ TAstToTextVisitor }
function TAstToTextVisitor.VisitAddSeriesItem(const Node: IAddSeriesItemNode): string;
var
seriesStr, valueStr, lookbackStr: string;
begin
seriesStr := Accept(Node.Series);
valueStr := Accept(Node.Value);
lookbackStr := '';
if Assigned(Node.Lookback) then
lookbackStr := ', ' + Accept(Node.Lookback);
Result := Format('%s.add(%s%s)', [seriesStr, valueStr, lookbackStr]);
end;
function TAstToTextVisitor.VisitAssignment(const Node: IAssignmentNode): string;
begin
Result := Node.Identifier.Name + ' := ' + Accept(Node.Value);
end;
function TAstToTextVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): string;
begin
var leftStr := Accept(Node.Left);
var rightStr := Accept(Node.Right);
Result := '(' + leftStr + ' ' + Node.Operator.ToString + ' ' + rightStr + ')';
end;
function TAstToTextVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): string;
begin
Result := '{...}';
end;
function TAstToTextVisitor.VisitConstant(const Node: IConstantNode): string;
begin
Result := Node.Value.ToString;
end;
function TAstToTextVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): string;
begin
Result := 'new series(' + Node.Definition + ')';
end;
function TAstToTextVisitor.VisitFunctionCall(const Node: IFunctionCallNode): string;
var
i: Integer;
sb: TStringBuilder;
calleeStr: string;
begin
calleeStr := Accept(Node.Callee);
sb := TStringBuilder.Create;
try
sb.Append(calleeStr);
sb.Append('(');
for i := 0 to High(Node.Arguments) do
begin
sb.Append(Accept(Node.Arguments[i]));
if i < High(Node.Arguments) then
sb.Append(', ');
end;
sb.Append(')');
Result := sb.ToString;
finally
sb.Free;
end;
end;
function TAstToTextVisitor.VisitMacroExpansionNode(const Node: IMacroExpansionNode): string;
begin
// To represent a macro expansion as a single line of text, the most
// informative representation is the original call itself. We can simply
// delegate to the VisitFunctionCall implementation to format it.
Result := VisitFunctionCall(Node.CallNode);
end;
function TAstToTextVisitor.VisitRecurNode(const Node: IRecurNode): string;
var
i: Integer;
sb: TStringBuilder;
begin
sb := TStringBuilder.Create;
try
sb.Append('recur(');
for i := 0 to High(Node.Arguments) do
begin
sb.Append(Accept(Node.Arguments[i]));
if i < High(Node.Arguments) then
sb.Append(', ');
end;
sb.Append(')');
Result := sb.ToString;
finally
sb.Free;
end;
end;
function TAstToTextVisitor.VisitIdentifier(const Node: IIdentifierNode): string;
begin
Result := Node.Name;
end;
function TAstToTextVisitor.VisitIfExpression(const Node: IIfExpressionNode): string;
begin
Result := Accept(Node.Condition);
end;
function TAstToTextVisitor.VisitIndexer(const Node: IIndexerNode): string;
var
baseStr, indexStr: string;
begin
baseStr := Accept(Node.Base);
indexStr := Accept(Node.Index);
Result := Format('%s[%s]', [baseStr, indexStr]);
end;
function TAstToTextVisitor.VisitKeyword(const Node: IKeywordNode): string;
begin
Result := ':' + Node.Value.Name;
end;
function TAstToTextVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): string;
var
i: Integer;
sb: TStringBuilder;
begin
sb := TStringBuilder.Create;
try
sb.Append(WideChar($03BB) + '('); // Lambda char
if Length(Node.Parameters) > 0 then
begin
for i := 0 to High(Node.Parameters) do
begin
sb.Append(Node.Parameters[i].Name);
if i < High(Node.Parameters) then
sb.Append(', ');
end;
end;
sb.Append(') => {...}');
Result := sb.ToString;
finally
sb.Free;
end;
end;
function TAstToTextVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): string;
var
i: Integer;
sb: TStringBuilder;
begin
// Added text representation for macro definitions.
sb := TStringBuilder.Create;
try
sb.Append('defmacro ');
sb.Append(Node.Name.Name);
sb.Append('(');
if Length(Node.Parameters) > 0 then
begin
for i := 0 to High(Node.Parameters) do
begin
sb.Append(Node.Parameters[i].Name);
if i < High(Node.Parameters) then
sb.Append(', ');
end;
end;
sb.Append(') => {...}');
Result := sb.ToString;
finally
sb.Free;
end;
end;
function TAstToTextVisitor.VisitMemberAccess(const Node: IMemberAccessNode): string;
var
baseStr: string;
begin
baseStr := Accept(Node.Base);
Result := Format('%s.%s', [baseStr, Node.Member.Value.Name]);
end;
function TAstToTextVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): string;
begin
Result := '`' + Accept(Node.Expression);
end;
function TAstToTextVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): string;
begin
Result := '{...}';
end;
function TAstToTextVisitor.VisitUnquote(const Node: IUnquoteNode): string;
begin
Result := '~' + Accept(Node.Expression);
end;
function TAstToTextVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): string;
begin
Result := '~@' + Accept(Node.Expression);
end;
function TAstToTextVisitor.VisitSeriesLength(const Node: ISeriesLengthNode): string;
var
seriesStr: string;
begin
seriesStr := Accept(Node.Series);
Result := Format('length(%s)', [seriesStr]);
end;
function TAstToTextVisitor.VisitTernaryExpression(const Node: ITernaryExpressionNode): string;
begin
var condStr := Accept(Node.Condition);
var thenStr := Accept(Node.ThenBranch);
var elseStr := Accept(Node.ElseBranch);
Result := Format('(%s ? %s : %s)', [condStr, thenStr, elseStr]);
end;
function TAstToTextVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): string;
begin
Result := Node.Operator.ToString + ' ' + Accept(Node.Right);
end;
function TAstToTextVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): string;
var
initStr: string;
begin
if Assigned(Node.Initializer) then
initStr := ' := ' + Accept(Node.Initializer)
else
initStr := '';
Result := 'var ' + Node.Identifier.Name + initStr;
end;
end.