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) protected function VisitConstant(const Node: IConstantNode): string; override; function VisitIdentifier(const Node: IIdentifierNode): string; override; function VisitKeyword(const Node: IKeywordNode): 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 := Accept(Node.Target) + ' := ' + Accept(Node.Value); 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.VisitVariableDeclaration(const Node: IVariableDeclarationNode): string; var initStr: string; begin if Assigned(Node.Initializer) then initStr := ' := ' + Accept(Node.Initializer) else initStr := ''; Result := 'var ' + Accept(Node.Target) + initStr; end; end.