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,