unit Myc.Ast.Json; interface uses System.SysUtils, System.Generics.Collections, System.JSON, Myc.Data.Scalar, Myc.Data.Value, Myc.Ast, Myc.Ast.Visitor, Myc.Ast.Nodes; type IJsonAstConverter = interface function Serialize(const RootNode: IAstNode): TJSONObject; function Deserialize(const AJson: TJSONObject): IAstNode; end; // TJsonAstConverter implements the visitor pattern for serialization // and uses factory functions for deserialization. // Inherits from the new generic visitor base class TJsonAstConverter = class(TAstVisitor, IJsonAstConverter) private procedure DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string); function JsonToNode(const AJson: TJSONValue; const ExpectedType: String = ''): IAstNode; function JsonToDataValue(const AObj: TJSONObject; const AName: string): TDataValue; function JsonToConstantNode(const AObj: TJSONObject): IConstantNode; function JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode; function JsonToKeywordNode(const AObj: TJSONObject): IKeywordNode; function JsonToIfExprNode(const AObj: TJSONObject): IIfExpressionNode; function JsonToCondExprNode(const AObj: TJSONObject): ICondExpressionNode; function JsonToTernaryExprNode(const AObj: TJSONObject): IAstNode; // Backward Compatibility function JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode; function JsonToMacroDefNode(const AObj: TJSONObject): IMacroDefinitionNode; function JsonToQuasiquoteNode(const AObj: TJSONObject): IQuasiquoteNode; function JsonToUnquoteNode(const AObj: TJSONObject): IUnquoteNode; function JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode; function JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode; function JsonToMacroExpansionNode(const AObj: TJSONObject): IMacroExpansionNode; function JsonToRecurNode(const AObj: TJSONObject): IRecurNode; function JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode; function JsonToVarDeclNode(const AObj: TJSONObject): IVariableDeclarationNode; function JsonToAssignmentNode(const AObj: TJSONObject): IAssignmentNode; function JsonToIndexerNode(const AObj: TJSONObject): IIndexerNode; function JsonToMemberAccessNode(const AObj: TJSONObject): IMemberAccessNode; function JsonToRecordLiteralNode(const AObj: TJSONObject): IRecordLiteralNode; function JsonToCreateSeriesNode(const AObj: TJSONObject): ICreateSeriesNode; function JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode; function JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode; function JsonToNopNode(const AObj: TJSONObject): INopNode; protected // IAstVisitor implementation for serialization (T = TJSONObject) function VisitConstant(const Node: IConstantNode): TJSONObject; override; function VisitIdentifier(const Node: IIdentifierNode): TJSONObject; override; function VisitKeyword(const Node: IKeywordNode): TJSONObject; override; // List Visitors function VisitParameterList(const Node: IParameterList): TJSONObject; override; function VisitArgumentList(const Node: IArgumentList): TJSONObject; override; function VisitExpressionList(const Node: IExpressionList): TJSONObject; override; function VisitRecordFieldList(const Node: IRecordFieldList): TJSONObject; override; function VisitRecordField(const Node: IRecordFieldNode): TJSONObject; override; function VisitIfExpression(const Node: IIfExpressionNode): TJSONObject; override; function VisitCondExpression(const Node: ICondExpressionNode): TJSONObject; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TJSONObject; override; function VisitMacroDefinition(const Node: IMacroDefinitionNode): TJSONObject; override; function VisitQuasiquote(const Node: IQuasiquoteNode): TJSONObject; override; function VisitUnquote(const Node: IUnquoteNode): TJSONObject; override; function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TJSONObject; override; function VisitFunctionCall(const Node: IFunctionCallNode): TJSONObject; override; function VisitMacroExpansionNode(const Node: IMacroExpansionNode): TJSONObject; override; function VisitRecurNode(const Node: IRecurNode): TJSONObject; override; function VisitBlockExpression(const Node: IBlockExpressionNode): TJSONObject; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TJSONObject; override; function VisitAssignment(const Node: IAssignmentNode): TJSONObject; override; function VisitIndexer(const Node: IIndexerNode): TJSONObject; override; function VisitMemberAccess(const Node: IMemberAccessNode): TJSONObject; override; function VisitRecordLiteral(const Node: IRecordLiteralNode): TJSONObject; override; function VisitCreateSeries(const Node: ICreateSeriesNode): TJSONObject; override; function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TJSONObject; override; function VisitSeriesLength(const Node: ISeriesLengthNode): TJSONObject; override; function VisitNop(const Node: INopNode): TJSONObject; override; function Serialize(const RootNode: IAstNode): TJSONObject; function Deserialize(const AJson: TJSONObject): IAstNode; public constructor Create; end; implementation uses Myc.Data.Keyword; { TJsonAstConverter } constructor TJsonAstConverter.Create; begin inherited; end; function TJsonAstConverter.Deserialize(const AJson: TJSONObject): IAstNode; begin Result := JsonToNode(AJson); end; { TJsonAstConverter - IAstVisitor for Serialization } procedure TJsonAstConverter.DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string); var valObj, scalarObj: TJSONObject; kwName: string; begin valObj := TJSONObject.Create; valObj.AddPair('Kind', TJSONString.Create(AValue.Kind.ToString)); case AValue.Kind of vkScalar: begin scalarObj := TJSONObject.Create; scalarObj.AddPair('Kind', TJSONString.Create(AValue.AsScalar.Kind.ToString)); case AValue.AsScalar.Kind of TScalar.TKind.Ordinal: scalarObj.AddPair('Value', TJSONNumber.Create(AValue.AsScalar.Value.AsInt64)); TScalar.TKind.Float: scalarObj.AddPair('Value', TJSONNumber.Create(AValue.AsScalar.Value.AsDouble)); TScalar.TKind.Boolean: scalarObj.AddPair('Value', TJSONBool.Create(AValue.AsScalar.Value.AsInt64 <> 0)); TScalar.TKind.DateTime: scalarObj.AddPair('Value', TJSONNumber.Create(AValue.AsScalar.Value.AsDouble)); TScalar.TKind.Keyword: begin // Use reverse map to get name from index (AsInt64) kwName := TKeywordRegistry.GetName(AValue.AsScalar.Value.AsInt64); scalarObj.AddPair('Value', TJSONString.Create(kwName)); end; end; valObj.AddPair('Value', scalarObj); end; vkText: valObj.AddPair('Value', TJSONString.Create(AValue.AsText)); vkVoid:; // No value to add else raise ENotSupportedException.Create('Unsupported TDataValue kind for constant serialization.'); end; AParent.AddPair(AName, valObj); end; function TJsonAstConverter.VisitConstant(const Node: IConstantNode): TJSONObject; begin Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Constant')); DataValueToJson(Node.Value, Result, 'Value'); end; function TJsonAstConverter.VisitIdentifier(const Node: IIdentifierNode): TJSONObject; begin Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Identifier')); Result.AddPair('Name', TJSONString.Create(Node.Name)); end; function TJsonAstConverter.VisitKeyword(const Node: IKeywordNode): TJSONObject; begin Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Keyword')); Result.AddPair('Name', TJSONString.Create(Node.Value.Name)); end; function TJsonAstConverter.VisitIfExpression(const Node: IIfExpressionNode): TJSONObject; var condObj, thenObj, elseObj: TJSONObject; begin condObj := Accept(Node.Condition); thenObj := Accept(Node.ThenBranch); elseObj := Accept(Node.ElseBranch); // Accept handles nil Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('IfExpr')); Result.AddPair('Condition', condObj); Result.AddPair('ThenBranch', thenObj); if Assigned(elseObj) then Result.AddPair('ElseBranch', elseObj) else Result.AddPair('ElseBranch', TJSONNull.Create); end; function TJsonAstConverter.VisitCondExpression(const Node: ICondExpressionNode): TJSONObject; var pairsArray: TJSONArray; pairObj: TJSONObject; elseObj: TJSONObject; i: Integer; begin pairsArray := TJSONArray.Create; for i := 0 to High(Node.Pairs) do begin pairObj := TJSONObject.Create; pairObj.AddPair('Condition', Accept(Node.Pairs[i].Condition)); pairObj.AddPair('Branch', Accept(Node.Pairs[i].Branch)); pairsArray.Add(pairObj); end; elseObj := Accept(Node.ElseBranch); // Accept handles nil Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('CondExpr')); Result.AddPair('Pairs', pairsArray); if Assigned(elseObj) then Result.AddPair('ElseBranch', elseObj) else Result.AddPair('ElseBranch', TJSONNull.Create); end; function TJsonAstConverter.VisitLambdaExpression(const Node: ILambdaExpressionNode): TJSONObject; var bodyObj: TJSONObject; paramsArray: TJSONArray; i: Integer; begin bodyObj := Accept(Node.Body); paramsArray := TJSONArray.Create; for i := 0 to Node.Parameters.Count - 1 do paramsArray.Add(Accept(Node.Parameters[i])); Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('LambdaExpr')); Result.AddPair('Parameters', paramsArray); Result.AddPair('Body', bodyObj); end; function TJsonAstConverter.VisitMacroDefinition(const Node: IMacroDefinitionNode): TJSONObject; var nameObj, bodyObj: TJSONObject; paramsArray: TJSONArray; i: Integer; begin nameObj := Accept(Node.Name); bodyObj := Accept(Node.Body); paramsArray := TJSONArray.Create; for i := 0 to Node.Parameters.Count - 1 do paramsArray.Add(Accept(Node.Parameters[i])); Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('MacroDef')); Result.AddPair('Name', nameObj); Result.AddPair('Parameters', paramsArray); Result.AddPair('Body', bodyObj); end; function TJsonAstConverter.VisitQuasiquote(const Node: IQuasiquoteNode): TJSONObject; begin Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Quasiquote')); Result.AddPair('Expression', Accept(Node.Expression)); end; function TJsonAstConverter.VisitUnquote(const Node: IUnquoteNode): TJSONObject; begin Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Unquote')); Result.AddPair('Expression', Accept(Node.Expression)); end; function TJsonAstConverter.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TJSONObject; begin Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('UnquoteSplicing')); Result.AddPair('Expression', Accept(Node.Expression)); end; function TJsonAstConverter.VisitFunctionCall(const Node: IFunctionCallNode): TJSONObject; var calleeObj: TJSONObject; argsArray: TJSONArray; i: Integer; begin calleeObj := Accept(Node.Callee); argsArray := TJSONArray.Create; for i := 0 to Node.Arguments.Count - 1 do argsArray.Add(Accept(Node.Arguments[i])); Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('FunctionCall')); Result.AddPair('Callee', calleeObj); Result.AddPair('Arguments', argsArray); end; function TJsonAstConverter.VisitMacroExpansionNode(const Node: IMacroExpansionNode): TJSONObject; var calleeObj, bodyObj: TJSONObject; argsArray: TJSONArray; i: Integer; begin calleeObj := Accept(Node.CallNode.Callee); bodyObj := Accept(Node.ExpandedBody); argsArray := TJSONArray.Create; for i := 0 to Node.CallNode.Arguments.Count - 1 do argsArray.Add(Accept(Node.CallNode.Arguments[i])); Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('MacroExpansion')); Result.AddPair('Callee', calleeObj); Result.AddPair('Arguments', argsArray); Result.AddPair('ExpandedBody', bodyObj); end; function TJsonAstConverter.VisitRecurNode(const Node: IRecurNode): TJSONObject; var argsArray: TJSONArray; i: Integer; begin argsArray := TJSONArray.Create; for i := 0 to Node.Arguments.Count - 1 do argsArray.Add(Accept(Node.Arguments[i])); Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Recur')); Result.AddPair('Arguments', argsArray); end; function TJsonAstConverter.VisitBlockExpression(const Node: IBlockExpressionNode): TJSONObject; var exprsArray: TJSONArray; i: Integer; begin exprsArray := TJSONArray.Create; for i := 0 to Node.Expressions.Count - 1 do exprsArray.Add(Accept(Node.Expressions[i])); Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Block')); Result.AddPair('Expressions', exprsArray); end; function TJsonAstConverter.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TJSONObject; var identObj, initObj: TJSONObject; begin identObj := Accept(Node.Target); initObj := Accept(Node.Initializer); // Accept handles nil Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('VarDecl')); Result.AddPair('Identifier', identObj); if Assigned(initObj) then Result.AddPair('Initializer', initObj) else Result.AddPair('Initializer', TJSONNull.Create); end; function TJsonAstConverter.VisitAssignment(const Node: IAssignmentNode): TJSONObject; var identObj, valueObj: TJSONObject; begin identObj := Accept(Node.Target); valueObj := Accept(Node.Value); Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Assignment')); Result.AddPair('Identifier', identObj); Result.AddPair('Value', valueObj); end; function TJsonAstConverter.VisitIndexer(const Node: IIndexerNode): TJSONObject; var baseObj, indexObj: TJSONObject; begin baseObj := Accept(Node.Base); indexObj := Accept(Node.Index); Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Indexer')); Result.AddPair('Base', baseObj); Result.AddPair('Index', indexObj); end; function TJsonAstConverter.VisitMemberAccess(const Node: IMemberAccessNode): TJSONObject; var baseObj, memberObj: TJSONObject; begin baseObj := Accept(Node.Base); memberObj := Accept(Node.Member); Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('MemberAccess')); Result.AddPair('Base', baseObj); Result.AddPair('Member', memberObj); end; function TJsonAstConverter.VisitRecordLiteral(const Node: IRecordLiteralNode): TJSONObject; var fieldsArray: TJSONArray; i: Integer; begin Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('RecordLiteral')); fieldsArray := TJSONArray.Create; for i := 0 to Node.Fields.Count - 1 do begin // We visit the RecordFieldNode, which returns a JSON Object fieldsArray.Add(Accept(Node.Fields[i])); end; Result.AddPair('Fields', fieldsArray); end; function TJsonAstConverter.VisitRecordField(const Node: IRecordFieldNode): TJSONObject; begin Result := TJSONObject.Create; Result.AddPair('Name', TJSONString.Create(Node.Key.Value.Name)); Result.AddPair('Value', Accept(Node.Value)); end; function TJsonAstConverter.VisitCreateSeries(const Node: ICreateSeriesNode): TJSONObject; begin Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('CreateSeries')); Result.AddPair('Definition', TJSONString.Create(Node.Definition)); end; function TJsonAstConverter.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TJSONObject; var seriesObj, valueObj, lookbackObj: TJSONObject; begin seriesObj := Accept(Node.Series); valueObj := Accept(Node.Value); lookbackObj := Accept(Node.Lookback); // Accept handles nil Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('AddSeriesItem')); Result.AddPair('Series', seriesObj); Result.AddPair('Value', valueObj); if Assigned(lookbackObj) then Result.AddPair('Lookback', lookbackObj) else Result.AddPair('Lookback', TJSONNull.Create); end; function TJsonAstConverter.VisitSeriesLength(const Node: ISeriesLengthNode): TJSONObject; var seriesObj: TJSONObject; begin seriesObj := Accept(Node.Series); Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('SeriesLength')); Result.AddPair('Series', seriesObj); end; function TJsonAstConverter.VisitNop(const Node: INopNode): TJSONObject; begin Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Nop')); end; // --- List Visitors (Placeholder return) --- function TJsonAstConverter.VisitParameterList(const Node: IParameterList): TJSONObject; begin Result := nil; end; function TJsonAstConverter.VisitArgumentList(const Node: IArgumentList): TJSONObject; begin Result := nil; end; function TJsonAstConverter.VisitExpressionList(const Node: IExpressionList): TJSONObject; begin Result := nil; end; function TJsonAstConverter.VisitRecordFieldList(const Node: IRecordFieldList): TJSONObject; begin Result := nil; end; // ------------------------------------------------------------------------------------------------ // Deserialization // ------------------------------------------------------------------------------------------------ function TJsonAstConverter.JsonToDataValue(const AObj: TJSONObject; const AName: string): TDataValue; var valObj, scalarObj: TJSONObject; kindStr: string; scalarKind: TScalar.TKind; begin valObj := AObj.GetValue(AName); kindStr := valObj.GetValue('Kind'); if SameText(kindStr, 'Scalar') then begin scalarObj := valObj.GetValue('Value'); kindStr := scalarObj.GetValue('Kind'); scalarKind := TScalar.StringToKind(kindStr); case scalarKind of TScalar.TKind.Ordinal: Result := TScalar.FromInt64(scalarObj.GetValue('Value').AsInt64); TScalar.TKind.Float: Result := TScalar.FromDouble(scalarObj.GetValue('Value').AsDouble); TScalar.TKind.Boolean: Result := TScalar.FromBoolean(scalarObj.GetValue('Value').AsBoolean); TScalar.TKind.DateTime: Result := TScalar.FromDateTime(scalarObj.GetValue('Value').AsDouble); TScalar.TKind.Keyword: // Keywords are serialized by name, intern them back Result := TScalar.FromKeyword(TKeywordRegistry.Intern(scalarObj.GetValue('Value'))); end; end else if SameText(kindStr, 'Text') then begin Result := valObj.GetValue('Value'); end else if SameText(kindStr, 'Void') then begin Result := TDataValue.Void; end else raise ENotSupportedException.Create('Unsupported TDataValue kind for constant deserialization.'); end; function TJsonAstConverter.JsonToConstantNode(const AObj: TJSONObject): IConstantNode; begin Result := TAst.Constant(JsonToDataValue(AObj, 'Value')); end; function TJsonAstConverter.JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode; begin Result := TAst.Identifier(AObj.GetValue('Name')); end; function TJsonAstConverter.JsonToKeywordNode(const AObj: TJSONObject): IKeywordNode; begin Result := TAst.Keyword(AObj.GetValue('Name')); end; function TJsonAstConverter.JsonToIfExprNode(const AObj: TJSONObject): IIfExpressionNode; var condNode, thenNode, elseNode: IAstNode; begin condNode := JsonToNode(AObj.GetValue('Condition')); thenNode := JsonToNode(AObj.GetValue('ThenBranch')); if not (AObj.GetValue('ElseBranch') is TJSONNull) then elseNode := JsonToNode(AObj.GetValue('ElseBranch')) else elseNode := nil; Result := TAst.IfExpr(condNode, thenNode, elseNode); end; function TJsonAstConverter.JsonToCondExprNode(const AObj: TJSONObject): ICondExpressionNode; var pairsArray: TJSONArray; pairs: TArray; elseNode: IAstNode; i: Integer; pairObj: TJSONObject; begin pairsArray := AObj.GetValue('Pairs'); SetLength(pairs, pairsArray.Count); for i := 0 to pairsArray.Count - 1 do begin pairObj := pairsArray.Items[i] as TJSONObject; pairs[i] := TCondPair.Create(JsonToNode(pairObj.GetValue('Condition')), JsonToNode(pairObj.GetValue('Branch'))); end; if not (AObj.GetValue('ElseBranch') is TJSONNull) then elseNode := JsonToNode(AObj.GetValue('ElseBranch')) else elseNode := nil; Result := TAst.CondExpr(pairs, elseNode); end; function TJsonAstConverter.JsonToTernaryExprNode(const AObj: TJSONObject): IAstNode; var condNode, thenNode, elseNode: IAstNode; begin condNode := JsonToNode(AObj.GetValue('Condition')); thenNode := JsonToNode(AObj.GetValue('ThenBranch')); elseNode := JsonToNode(AObj.GetValue('ElseBranch')); // Map to CondExpr using the helper in TAst Result := TAst.TernaryExpr(condNode, thenNode, elseNode); end; function TJsonAstConverter.JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode; var params: TArray; body: IAstNode; paramArray: TJSONArray; i: Integer; begin paramArray := AObj.GetValue('Parameters'); SetLength(params, paramArray.Count); for i := 0 to paramArray.Count - 1 do params[i] := JsonToIdentifierNode(paramArray.Items[i] as TJSONObject); body := JsonToNode(AObj.GetValue('Body')); Result := TAst.LambdaExpr(params, body); end; function TJsonAstConverter.JsonToMacroDefNode(const AObj: TJSONObject): IMacroDefinitionNode; var name: IIdentifierNode; params: TArray; body: IAstNode; paramArray: TJSONArray; i: Integer; begin name := JsonToIdentifierNode(AObj.GetValue('Name') as TJSONObject); paramArray := AObj.GetValue('Parameters'); SetLength(params, paramArray.Count); for i := 0 to paramArray.Count - 1 do params[i] := JsonToIdentifierNode(paramArray.Items[i] as TJSONObject); body := JsonToNode(AObj.GetValue('Body'), 'Quasiquote'); Result := TAst.MacroDef(name, params, body); end; function TJsonAstConverter.JsonToMacroExpansionNode(const AObj: TJSONObject): IMacroExpansionNode; var callee, expandedBody: IAstNode; args: TArray; argsArray: TJSONArray; i: Integer; tempCallNode: IFunctionCallNode; begin callee := JsonToNode(AObj.GetValue('Callee')); expandedBody := JsonToNode(AObj.GetValue('ExpandedBody')); argsArray := AObj.GetValue('Arguments'); SetLength(args, argsArray.Count); for i := 0 to argsArray.Count - 1 do args[i] := JsonToNode(argsArray.Items[i]); tempCallNode := TAst.FunctionCall(callee, args); Result := TAst.MacroExpansionNode(tempCallNode, expandedBody); end; function TJsonAstConverter.JsonToQuasiquoteNode(const AObj: TJSONObject): IQuasiquoteNode; begin Result := TAst.Quasiquote(JsonToNode(AObj.GetValue('Expression'))); end; function TJsonAstConverter.JsonToUnquoteNode(const AObj: TJSONObject): IUnquoteNode; begin Result := TAst.Unquote(JsonToNode(AObj.GetValue('Expression'))); end; function TJsonAstConverter.JsonToUnquoteSplicingNode(const AObj: TJSONObject): IUnquoteSplicingNode; begin Result := TAst.UnquoteSplicing(JsonToNode(AObj.GetValue('Expression')).AsQuasiquote); end; function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode; var callee: IAstNode; args: TArray; argsArray: TJSONArray; i: Integer; begin callee := JsonToNode(AObj.GetValue('Callee')); argsArray := AObj.GetValue('Arguments'); SetLength(args, argsArray.Count); for i := 0 to argsArray.Count - 1 do args[i] := JsonToNode(argsArray.Items[i]); Result := TAst.FunctionCall(callee, args); end; function TJsonAstConverter.JsonToRecurNode(const AObj: TJSONObject): IRecurNode; var args: TArray; argsArray: TJSONArray; i: Integer; begin argsArray := AObj.GetValue('Arguments'); SetLength(args, argsArray.Count); for i := 0 to argsArray.Count - 1 do args[i] := JsonToNode(argsArray.Items[i]); Result := TAst.Recur(args); end; function TJsonAstConverter.JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode; var expressions: TArray; exprsArray: TJSONArray; i: Integer; begin exprsArray := AObj.GetValue('Expressions'); SetLength(expressions, exprsArray.Count); for i := 0 to exprsArray.Count - 1 do expressions[i] := JsonToNode(exprsArray.Items[i]); Result := TAst.Block(expressions); end; function TJsonAstConverter.JsonToVarDeclNode(const AObj: TJSONObject): IVariableDeclarationNode; var ident: IIdentifierNode; initializer: IAstNode; begin ident := JsonToIdentifierNode(AObj.GetValue('Identifier') as TJSONObject); if not (AObj.GetValue('Initializer') is TJSONNull) then initializer := JsonToNode(AObj.GetValue('Initializer')) else initializer := nil; Result := TAst.VarDecl(ident, initializer); end; function TJsonAstConverter.JsonToAssignmentNode(const AObj: TJSONObject): IAssignmentNode; var ident: IIdentifierNode; value: IAstNode; begin ident := JsonToIdentifierNode(AObj.GetValue('Identifier') as TJSONObject); value := JsonToNode(AObj.GetValue('Value')); Result := TAst.Assign(ident, value); end; function TJsonAstConverter.JsonToIndexerNode(const AObj: TJSONObject): IIndexerNode; begin Result := TAst.Indexer(JsonToNode(AObj.GetValue('Base')), JsonToNode(AObj.GetValue('Index'))); end; function TJsonAstConverter.JsonToMemberAccessNode(const AObj: TJSONObject): IMemberAccessNode; begin Result := TAst.MemberAccess(JsonToNode(AObj.GetValue('Base')), JsonToKeywordNode(AObj.GetValue('Member') as TJSONObject)); end; function TJsonAstConverter.JsonToRecordLiteralNode(const AObj: TJSONObject): IRecordLiteralNode; var fields: TArray; fieldsArray: TJSONArray; fieldObj: TJSONObject; i: Integer; keyNode: IKeywordNode; valueNode: IAstNode; begin fieldsArray := AObj.GetValue('Fields'); SetLength(fields, fieldsArray.Count); for i := 0 to fieldsArray.Count - 1 do begin fieldObj := fieldsArray.Items[i] as TJSONObject; keyNode := TAst.Keyword(fieldObj.GetValue('Name')); valueNode := JsonToNode(fieldObj.GetValue('Value')); fields[i] := TAst.RecordField(keyNode, valueNode); end; Result := TAst.RecordLiteral(fields); end; function TJsonAstConverter.JsonToCreateSeriesNode(const AObj: TJSONObject): ICreateSeriesNode; begin Result := TAst.CreateSeries(AObj.GetValue('Definition')); end; function TJsonAstConverter.JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode; var lookbackNode: IAstNode; begin if not (AObj.GetValue('Lookback') is TJSONNull) then lookbackNode := JsonToNode(AObj.GetValue('Lookback')) else lookbackNode := nil; Result := TAst.AddSeriesItem(JsonToIdentifierNode(AObj.GetValue('Series') as TJSONObject), JsonToNode(AObj.GetValue('Value')), lookbackNode); end; function TJsonAstConverter.JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode; begin Result := TAst.SeriesLength(JsonToIdentifierNode(AObj.GetValue('Series') as TJSONObject)); end; function TJsonAstConverter.JsonToNopNode(const AObj: TJSONObject): INopNode; begin Result := TAst.Nop.AsNop; end; function TJsonAstConverter.JsonToNode(const AJson: TJSONValue; const ExpectedType: String = ''): IAstNode; var obj: TJSONObject; nodeType: string; begin if not (AJson is TJSONObject) then raise EInvalidCast.Create('Expected a JSON object for node deserialization.'); obj := AJson as TJSONObject; nodeType := obj.GetValue('NodeType'); if (ExpectedType <> '') and (nodeType <> ExpectedType) then raise EInvalidCast.CreateFmt('Expected a JSON object of type %s for deserialization, but got a %s.', [ExpectedType, nodeType]); if nodeType = 'Constant' then Result := JsonToConstantNode(obj) else if nodeType = 'Identifier' then Result := JsonToIdentifierNode(obj) else if nodeType = 'Keyword' then Result := JsonToKeywordNode(obj) else if nodeType = 'IfExpr' then Result := JsonToIfExprNode(obj) else if nodeType = 'CondExpr' then Result := JsonToCondExprNode(obj) else if nodeType = 'TernaryExpr' then Result := JsonToTernaryExprNode(obj) // Backward Compatibility else if nodeType = 'LambdaExpr' then Result := JsonToLambdaExprNode(obj) else if nodeType = 'MacroDef' then Result := JsonToMacroDefNode(obj) else if nodeType = 'MacroExpansion' then Result := JsonToMacroExpansionNode(obj) else if nodeType = 'Quasiquote' then Result := JsonToQuasiquoteNode(obj) else if nodeType = 'Unquote' then Result := JsonToUnquoteNode(obj) else if nodeType = 'UnquoteSplicing' then Result := JsonToUnquoteSplicingNode(obj) else if nodeType = 'FunctionCall' then Result := JsonToFunctionCallNode(obj) else if nodeType = 'Recur' then Result := JsonToRecurNode(obj) else if nodeType = 'Block' then Result := JsonToBlockNode(obj) else if nodeType = 'VarDecl' then Result := JsonToVarDeclNode(obj) else if nodeType = 'Assignment' then Result := JsonToAssignmentNode(obj) else if nodeType = 'Indexer' then Result := JsonToIndexerNode(obj) else if nodeType = 'MemberAccess' then Result := JsonToMemberAccessNode(obj) else if nodeType = 'RecordLiteral' then Result := JsonToRecordLiteralNode(obj) else if nodeType = 'CreateSeries' then Result := JsonToCreateSeriesNode(obj) else if nodeType = 'AddSeriesItem' then Result := JsonToAddSeriesItemNode(obj) else if nodeType = 'SeriesLength' then Result := JsonToSeriesLengthNode(obj) else if nodeType = 'Nop' then Result := JsonToNopNode(obj) else raise ENotSupportedException.CreateFmt('Unsupported NodeType "%s" for JSON deserialization.', [nodeType]); end; function TJsonAstConverter.Serialize(const RootNode: IAstNode): TJSONObject; begin if not Assigned(RootNode) then exit(nil); // Call Accept, which returns TDataValue(vkGeneric(TJSONObject)) // and unwrap it. Result := RootNode.Accept(Self).AsGeneric; end; end.