From 695e854cc3ba9e151af1b73519a63af5f8219002 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Fri, 12 Sep 2025 08:51:52 +0200 Subject: [PATCH] Ast JSON --- ASTPlayground/MainForm.pas | 67 +-- AuraTrader/AuraTrader.dproj | 2 +- Src/AST/Myc.Ast.Evaluator.pas | 1 + Src/AST/Myc.Ast.JSON.pas | 744 ++++++++++++++++++++++++++++ Src/Myc.Trade.Indicators.Common.pas | 1 - 5 files changed, 783 insertions(+), 32 deletions(-) create mode 100644 Src/AST/Myc.Ast.JSON.pas diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index 2d5ee62..e2c8999 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -27,7 +27,6 @@ uses Myc.Ast, Myc.Ast.Evaluator, Myc.Ast.Printer, - Myc.Ast.Persistence, FMX.Layouts, FMX.Objects, Myc.Ast.Scope; // Added for TExecutionScope @@ -99,7 +98,8 @@ uses // Myc.Ast.Scope is now in the interface uses Myc.Data.Scalar.JSON, Myc.Data.Decimal, - System.Diagnostics; // For TStopwatch + System.Diagnostics, // For TStopwatch + Myc.Ast.Json; // For TAstJson serialization {$R *.fmx} @@ -646,55 +646,62 @@ begin end; procedure TForm1.FromJSONButtonClick(Sender: TObject); +var + jsonString: string; begin - // Reads an AST JSON string from the memo and deserializes it into FLastAst. - if Memo1.Lines.Text.IsEmpty then - begin - Memo1.Lines.Text := 'Memo is empty. Paste an AST JSON structure here first.'; - exit; - end; - + // Implemented TODO: Reads an AST JSON string from the memo and deserializes it into FLastAst. + Memo1.Lines.BeginUpdate; try - Memo1.Lines.Insert(0, '--- Deserializing AST from JSON ---'); - FLastAst := TAstProjectPersistence.JsonStringToAstNode(Memo1.Lines.Text); - if Assigned(FLastAst) then + jsonString := Memo1.Lines.Text; + Memo1.Lines.Clear; + + if jsonString.IsEmpty then begin - Memo1.Lines.Add('--- Deserialization successful. ---'); - Memo1.Lines.Add('Middle-click on the right panel to visualize the new AST.'); - end - else - begin - Memo1.Lines.Add('--- Deserialization failed. Invalid JSON or data structure. ---'); + Memo1.Lines.Add('Memo is empty. Please paste an AST JSON string.'); + exit; end; - except - on E: Exception do - begin - Memo1.Lines.Add('--- ERROR during deserialization ---'); - Memo1.Lines.Add(E.ClassName + ': ' + E.Message); + + try + FLastAst := TAstJson.Deserialize(jsonString); + Memo1.Lines.Add('AST deserialized successfully from JSON.'); + Memo1.Lines.Add('You can now visualize it (Middle Mouse Click) or pretty-print it.'); + except + on E: Exception do + begin + FLastAst := nil; + Memo1.Lines.Add('Error deserializing AST from JSON:'); + Memo1.Lines.Add(E.Message); + // Restore original text for correction + Memo1.Lines.Add('--- Original JSON ---'); + Memo1.Lines.Text := Memo1.Lines.Text + sLineBreak + jsonString; + end; end; + finally + Memo1.Lines.EndUpdate; end; end; procedure TForm1.ToJSONButtonClick(Sender: TObject); +var + jsonString: string; begin - // Serializes the current FLastAst to a JSON string and displays it in the memo. + // Implemented TODO: Serializes the current FLastAst to a JSON string and displays it in the memo. Memo1.Lines.Clear; - Memo1.Lines.Add('--- Serializing AST to JSON ---'); if not Assigned(FLastAst) then begin - Memo1.Lines.Add('No AST available to serialize. Generate one first.'); + Memo1.Lines.Add('No AST available to serialize. Please generate one first.'); exit; end; try - Memo1.Lines.Text := TAstProjectPersistence.AstNodeToJsonString(FLastAst); + jsonString := TAstJson.Serialize(FLastAst); + Memo1.Lines.Text := jsonString; except on E: Exception do begin - Memo1.Lines.Clear; - Memo1.Lines.Add('--- ERROR during serialization ---'); - Memo1.Lines.Add(E.ClassName + ': ' + E.Message); + Memo1.Lines.Add('Error serializing AST to JSON:'); + Memo1.Lines.Add(E.Message); end; end; end; diff --git a/AuraTrader/AuraTrader.dproj b/AuraTrader/AuraTrader.dproj index b476b63..0383d45 100644 --- a/AuraTrader/AuraTrader.dproj +++ b/AuraTrader/AuraTrader.dproj @@ -70,7 +70,7 @@ $(BDS)\bin\delphi_PROJECTICON.ico $(BDS)\bin\delphi_PROJECTICNS.icns AuraTrader - T:\Myc\Src;T:\Myc\Src\Data;$(DCC_UnitSearchPath) + T:\Myc\Src;T:\Myc\Src\Data;T:\Myc\Src\Ast;$(DCC_UnitSearchPath) 1031 CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index c1749cc..96a91d6 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -212,6 +212,7 @@ end; function TNativeClosure.GetUpvalues: TArray; begin + Result := nil; end; { TEvaluatorVisitor } diff --git a/Src/AST/Myc.Ast.JSON.pas b/Src/AST/Myc.Ast.JSON.pas new file mode 100644 index 0000000..d45fba8 --- /dev/null +++ b/Src/AST/Myc.Ast.JSON.pas @@ -0,0 +1,744 @@ +unit Myc.Ast.Json; + +interface + +uses + System.SysUtils, + Myc.Ast.Nodes; + +type + // TAstJson provides static methods for serializing and deserializing an AST to/from JSON. + TAstJson = record + public + class function Serialize(const ANode: IAstNode): string; static; + class function Deserialize(const AJson: string): IAstNode; static; + end; + +implementation + +uses + System.JSON, + System.Generics.Collections, + Myc.Ast, + Myc.Data.Scalar; + +type + // TJsonAstConverter implements the visitor pattern for serialization + // and uses factory functions for deserialization. + TJsonAstConverter = class(TInterfacedObject, IAstVisitor) + private + FJsonObjectStack: TStack; + procedure ScalarToJson(const AScalar: TScalar; const AParent: TJSONObject; const AName: string); + function JsonToNode(const AJson: TJSONValue): IAstNode; + function JsonToScalar(const AObj: TJSONObject; const AName: string): TScalar; + // Updated signatures to return specific node interface types + function JsonToConstantNode(const AObj: TJSONObject): IConstantNode; + function JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode; + function JsonToBinaryExprNode(const AObj: TJSONObject): IBinaryExpressionNode; + function JsonToUnaryExprNode(const AObj: TJSONObject): IUnaryExpressionNode; + function JsonToIfExprNode(const AObj: TJSONObject): IIfExpressionNode; + function JsonToTernaryExprNode(const AObj: TJSONObject): ITernaryExpressionNode; + function JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode; + function JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode; + 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 JsonToCreateSeriesNode(const AObj: TJSONObject): ICreateSeriesNode; + function JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode; + function JsonToSeriesLengthNode(const AObj: TJSONObject): ISeriesLengthNode; + protected + // IAstVisitor implementation for serialization + function VisitConstant(const Node: IConstantNode): TAstValue; + function VisitIdentifier(const Node: IIdentifierNode): TAstValue; + function VisitBinaryExpression(const Node: IBinaryExpressionNode): TAstValue; + function VisitUnaryExpression(const Node: IUnaryExpressionNode): TAstValue; + function VisitIfExpression(const Node: IIfExpressionNode): TAstValue; + function VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue; + function VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue; + function VisitFunctionCall(const Node: IFunctionCallNode): TAstValue; + function VisitBlockExpression(const Node: IBlockExpressionNode): TAstValue; + function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; + function VisitAssignment(const Node: IAssignmentNode): TAstValue; + function VisitIndexer(const Node: IIndexerNode): TAstValue; + function VisitMemberAccess(const Node: IMemberAccessNode): TAstValue; + function VisitCreateSeries(const Node: ICreateSeriesNode): TAstValue; + function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TAstValue; + function VisitSeriesLength(const Node: ISeriesLengthNode): TAstValue; + public + constructor Create; + destructor Destroy; override; + function Serialize(const ANode: IAstNode): string; + function Deserialize(const AJson: string): IAstNode; + end; + +{ TJsonAstConverter } + +constructor TJsonAstConverter.Create; +begin + inherited; + FJsonObjectStack := TStack.Create; +end; + +destructor TJsonAstConverter.Destroy; +begin + FJsonObjectStack.Free; + inherited; +end; + +function TJsonAstConverter.Serialize(const ANode: IAstNode): string; +var + rootObj: TJSONObject; +begin + if not Assigned(ANode) then + exit(''); + + FJsonObjectStack.Clear; + ANode.Accept(Self); + + if FJsonObjectStack.Count <> 1 then + raise EInvalidOpException.Create('JSON serialization stack is corrupt.'); + + rootObj := FJsonObjectStack.Pop; + try + Result := rootObj.Format(4); + finally + rootObj.Free; + end; +end; + +function TJsonAstConverter.Deserialize(const AJson: string): IAstNode; +var + jsonValue: TJSONValue; +begin + if AJson.IsEmpty then + exit(nil); + + jsonValue := TJSONObject.ParseJSONValue(AJson); + if jsonValue = nil then + raise EJSONParseException.Create('Invalid JSON format.'); + + try + Result := JsonToNode(jsonValue); + finally + jsonValue.Free; + end; +end; + +{ TJsonAstConverter - IAstVisitor for Serialization } + +procedure TJsonAstConverter.ScalarToJson(const AScalar: TScalar; const AParent: TJSONObject; const AName: string); +var + scalarObj: TJSONObject; +begin + scalarObj := TJSONObject.Create; + scalarObj.AddPair('Kind', TJSONString.Create(AScalar.Kind.ToString)); + + case AScalar.Kind of + skInt64: scalarObj.AddPair('Value', TJSONNumber.Create(AScalar.Value.AsInt64)); + skDouble: scalarObj.AddPair('Value', TJSONNumber.Create(AScalar.Value.AsDouble)); + skBoolean: scalarObj.AddPair('Value', TJSONBool.Create(AScalar.Value.AsBoolean)); + skString: scalarObj.AddPair('Value', TJSONString.Create(AScalar.Value.AsString)); + else + raise ENotSupportedException.CreateFmt('Scalar kind %s not supported for JSON serialization.', [AScalar.Kind.ToString]); + end; + + AParent.AddPair(AName, scalarObj); +end; + +function TJsonAstConverter.VisitConstant(const Node: IConstantNode): TAstValue; +var + obj: TJSONObject; +begin + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('Constant')); + ScalarToJson(Node.Value, obj, 'Value'); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitIdentifier(const Node: IIdentifierNode): TAstValue; +var + obj: TJSONObject; +begin + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('Identifier')); + obj.AddPair('Name', TJSONString.Create(Node.Name)); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitBinaryExpression(const Node: IBinaryExpressionNode): TAstValue; +var + obj, leftObj, rightObj: TJSONObject; +begin + Node.Left.Accept(Self); + Node.Right.Accept(Self); + + rightObj := FJsonObjectStack.Pop; + leftObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('BinaryExpr')); + obj.AddPair('Operator', TJSONString.Create(Node.Operator.ToString)); + obj.AddPair('Left', leftObj); + obj.AddPair('Right', rightObj); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitUnaryExpression(const Node: IUnaryExpressionNode): TAstValue; +var + obj, rightObj: TJSONObject; +begin + Node.Right.Accept(Self); + rightObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('UnaryExpr')); + obj.AddPair('Operator', TJSONString.Create(Node.Operator.ToString)); + obj.AddPair('Right', rightObj); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitIfExpression(const Node: IIfExpressionNode): TAstValue; +var + obj, condObj, thenObj, elseObj: TJSONObject; +begin + Node.Condition.Accept(Self); + Node.ThenBranch.Accept(Self); + if Assigned(Node.ElseBranch) then + Node.ElseBranch.Accept(Self); + + if Assigned(Node.ElseBranch) then + elseObj := FJsonObjectStack.Pop + else + elseObj := nil; + + thenObj := FJsonObjectStack.Pop; + condObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('IfExpr')); + obj.AddPair('Condition', condObj); + obj.AddPair('ThenBranch', thenObj); + if Assigned(elseObj) then + obj.AddPair('ElseBranch', elseObj) + else + obj.AddPair('ElseBranch', TJSONNull.Create); + + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitTernaryExpression(const Node: ITernaryExpressionNode): TAstValue; +var + obj, condObj, thenObj, elseObj: TJSONObject; +begin + Node.Condition.Accept(Self); + Node.ThenBranch.Accept(Self); + Node.ElseBranch.Accept(Self); + + elseObj := FJsonObjectStack.Pop; + thenObj := FJsonObjectStack.Pop; + condObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('TernaryExpr')); + obj.AddPair('Condition', condObj); + obj.AddPair('ThenBranch', thenObj); + obj.AddPair('ElseBranch', elseObj); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitLambdaExpression(const Node: ILambdaExpressionNode): TAstValue; +var + obj, bodyObj: TJSONObject; + paramsArray: TJSONArray; + tempParams: TArray; + param: IIdentifierNode; + i: Integer; +begin + for param in Node.Parameters do + param.Accept(Self); + Node.Body.Accept(Self); + + bodyObj := FJsonObjectStack.Pop; + + SetLength(tempParams, Length(Node.Parameters)); + for i := High(tempParams) downto 0 do + tempParams[i] := FJsonObjectStack.Pop; + + paramsArray := TJSONArray.Create; + for i := 0 to High(tempParams) do + paramsArray.Add(tempParams[i]); + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('LambdaExpr')); + obj.AddPair('Parameters', paramsArray); + obj.AddPair('Body', bodyObj); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitFunctionCall(const Node: IFunctionCallNode): TAstValue; +var + obj, calleeObj: TJSONObject; + argsArray: TJSONArray; + tempArgs: TArray; + arg: IAstNode; + i: Integer; +begin + Node.Callee.Accept(Self); + for arg in Node.Arguments do + arg.Accept(Self); + + SetLength(tempArgs, Node.Arguments.Count); + for i := High(tempArgs) downto 0 do + tempArgs[i] := FJsonObjectStack.Pop; + + argsArray := TJSONArray.Create; + for i := 0 to High(tempArgs) do + argsArray.Add(tempArgs[i]); + + calleeObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('FunctionCall')); + obj.AddPair('Callee', calleeObj); + obj.AddPair('Arguments', argsArray); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitBlockExpression(const Node: IBlockExpressionNode): TAstValue; +var + obj: TJSONObject; + exprsArray: TJSONArray; + tempExprs: TArray; + expr: IAstNode; + i: Integer; +begin + for expr in Node.Expressions do + expr.Accept(Self); + + SetLength(tempExprs, Node.Expressions.Count); + for i := High(tempExprs) downto 0 do + tempExprs[i] := FJsonObjectStack.Pop; + + exprsArray := TJSONArray.Create; + for i := 0 to High(tempExprs) do + exprsArray.Add(tempExprs[i]); + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('Block')); + obj.AddPair('Expressions', exprsArray); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TAstValue; +var + obj, identObj, initObj: TJSONObject; +begin + Node.Identifier.Accept(Self); + if Assigned(Node.Initializer) then + Node.Initializer.Accept(Self); + + if Assigned(Node.Initializer) then + initObj := FJsonObjectStack.Pop + else + initObj := nil; + identObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('VarDecl')); + obj.AddPair('Identifier', identObj); + if Assigned(initObj) then + obj.AddPair('Initializer', initObj) + else + obj.AddPair('Initializer', TJSONNull.Create); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitAssignment(const Node: IAssignmentNode): TAstValue; +var + obj, identObj, valueObj: TJSONObject; +begin + Node.Identifier.Accept(Self); + Node.Value.Accept(Self); + + valueObj := FJsonObjectStack.Pop; + identObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('Assignment')); + obj.AddPair('Identifier', identObj); + obj.AddPair('Value', valueObj); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitIndexer(const Node: IIndexerNode): TAstValue; +var + obj, baseObj, indexObj: TJSONObject; +begin + Node.Base.Accept(Self); + Node.Index.Accept(Self); + + indexObj := FJsonObjectStack.Pop; + baseObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('Indexer')); + obj.AddPair('Base', baseObj); + obj.AddPair('Index', indexObj); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitMemberAccess(const Node: IMemberAccessNode): TAstValue; +var + obj, baseObj, memberObj: TJSONObject; +begin + Node.Base.Accept(Self); + Node.Member.Accept(Self); + + memberObj := FJsonObjectStack.Pop; + baseObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('MemberAccess')); + obj.AddPair('Base', baseObj); + obj.AddPair('Member', memberObj); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitCreateSeries(const Node: ICreateSeriesNode): TAstValue; +var + obj: TJSONObject; +begin + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('CreateSeries')); + obj.AddPair('Definition', TJSONString.Create(Node.Definition)); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitAddSeriesItem(const Node: IAddSeriesItemNode): TAstValue; +var + obj, seriesObj, valueObj, lookbackObj: TJSONObject; +begin + Node.Series.Accept(Self); + Node.Value.Accept(Self); + if Assigned(Node.Lookback) then + Node.Lookback.Accept(Self); + + if Assigned(Node.Lookback) then + lookbackObj := FJsonObjectStack.Pop + else + lookbackObj := nil; + valueObj := FJsonObjectStack.Pop; + seriesObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('AddSeriesItem')); + obj.AddPair('Series', seriesObj); + obj.AddPair('Value', valueObj); + if Assigned(lookbackObj) then + obj.AddPair('Lookback', lookbackObj) + else + obj.AddPair('Lookback', TJSONNull.Create); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +function TJsonAstConverter.VisitSeriesLength(const Node: ISeriesLengthNode): TAstValue; +var + obj, seriesObj: TJSONObject; +begin + Node.Series.Accept(Self); + seriesObj := FJsonObjectStack.Pop; + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('SeriesLength')); + obj.AddPair('Series', seriesObj); + FJsonObjectStack.Push(obj); + Result := TAstValue.Void; +end; + +{ TJsonAstConverter - Deserialization } + +function TJsonAstConverter.JsonToScalar(const AObj: TJSONObject; const AName: string): TScalar; +var + scalarObj: TJSONObject; + kindStr: string; + scalarKind: TScalarKind; + jsonValue: TJSONValue; +begin + scalarObj := AObj.GetValue(AName); + kindStr := scalarObj.GetValue('Kind'); + scalarKind := TScalar.StringToKind(kindStr); + + jsonValue := scalarObj.GetValue('Value'); + + case scalarKind of + skInt64: Result := TScalar.FromInt64((jsonValue as TJSONNumber).AsInt64); + skDouble: Result := TScalar.FromDouble((jsonValue as TJSONNumber).AsDouble); + skBoolean: Result := TScalar.FromBoolean((jsonValue as TJSONBool).AsBoolean); + skString: Result := TScalar.FromString((jsonValue as TJSONString).Value); + else + raise ENotSupportedException.CreateFmt('Scalar kind %s not supported for JSON deserialization.', [kindStr]); + end; +end; + +function TJsonAstConverter.JsonToConstantNode(const AObj: TJSONObject): IConstantNode; +begin + Result := TAst.Constant(JsonToScalar(AObj, 'Value')); +end; + +function TJsonAstConverter.JsonToIdentifierNode(const AObj: TJSONObject): IIdentifierNode; +begin + Result := TAst.Identifier(AObj.GetValue('Name')); +end; + +function TJsonAstConverter.JsonToBinaryExprNode(const AObj: TJSONObject): IBinaryExpressionNode; +var + opStr: string; + op: TBinaryOperator; + leftNode, rightNode: IAstNode; +begin + opStr := AObj.GetValue('Operator'); + for op := Low(TBinaryOperator) to High(TBinaryOperator) do + if SameText(op.ToString, opStr) then + begin + leftNode := JsonToNode(AObj.GetValue('Left')); + rightNode := JsonToNode(AObj.GetValue('Right')); + Result := TAst.BinaryExpr(leftNode, op, rightNode); + exit; + end; + raise EInvalidOpException.CreateFmt('Unknown binary operator "%s"', [opStr]); +end; + +function TJsonAstConverter.JsonToUnaryExprNode(const AObj: TJSONObject): IUnaryExpressionNode; +var + opStr: string; + op: TUnaryOperator; + rightNode: IAstNode; +begin + opStr := AObj.GetValue('Operator'); + for op := Low(TUnaryOperator) to High(TUnaryOperator) do + if SameText(op.ToString, opStr) then + begin + rightNode := JsonToNode(AObj.GetValue('Right')); + Result := TAst.UnaryExpr(op, rightNode); + exit; + end; + raise EInvalidOpException.CreateFmt('Unknown unary operator "%s"', [opStr]); +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.JsonToTernaryExprNode(const AObj: TJSONObject): ITernaryExpressionNode; +var + condNode, thenNode, elseNode: IAstNode; +begin + condNode := JsonToNode(AObj.GetValue('Condition')); + thenNode := JsonToNode(AObj.GetValue('ThenBranch')); + elseNode := JsonToNode(AObj.GetValue('ElseBranch')); + 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.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.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')), JsonToIdentifierNode(AObj.GetValue('Member') as TJSONObject)); +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.JsonToNode(const AJson: TJSONValue): 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 nodeType = 'Constant' then + Result := JsonToConstantNode(obj) + else if nodeType = 'Identifier' then + Result := JsonToIdentifierNode(obj) + else if nodeType = 'BinaryExpr' then + Result := JsonToBinaryExprNode(obj) + else if nodeType = 'UnaryExpr' then + Result := JsonToUnaryExprNode(obj) + else if nodeType = 'IfExpr' then + Result := JsonToIfExprNode(obj) + else if nodeType = 'TernaryExpr' then + Result := JsonToTernaryExprNode(obj) + else if nodeType = 'LambdaExpr' then + Result := JsonToLambdaExprNode(obj) + else if nodeType = 'FunctionCall' then + Result := JsonToFunctionCallNode(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 = 'CreateSeries' then + Result := JsonToCreateSeriesNode(obj) + else if nodeType = 'AddSeriesItem' then + Result := JsonToAddSeriesItemNode(obj) + else if nodeType = 'SeriesLength' then + Result := JsonToSeriesLengthNode(obj) + else + raise ENotSupportedException.CreateFmt('Unsupported NodeType "%s" for JSON deserialization.', [nodeType]); +end; + +{ TAstJson } + +class function TAstJson.Deserialize(const AJson: string): IAstNode; +var + converter: TJsonAstConverter; +begin + converter := TJsonAstConverter.Create; + try + Result := converter.Deserialize(AJson); + finally + converter.Free; + end; +end; + +class function TAstJson.Serialize(const ANode: IAstNode): string; +var + converter: TJsonAstConverter; +begin + converter := TJsonAstConverter.Create; + try + Result := converter.Serialize(ANode); + finally + converter.Free; + end; +end; + +end. diff --git a/Src/Myc.Trade.Indicators.Common.pas b/Src/Myc.Trade.Indicators.Common.pas index e250f7e..6afd417 100644 --- a/Src/Myc.Trade.Indicators.Common.pas +++ b/Src/Myc.Trade.Indicators.Common.pas @@ -6,7 +6,6 @@ uses System.SysUtils, System.Math, System.Rtti, - Myc.Data.Records, Myc.Data.Pipeline, Myc.Data.Series, Myc.Data.Types,