diff --git a/ASTPlayground/ASTPlayground.dpr b/ASTPlayground/ASTPlayground.dpr index d04cda8..21890c4 100644 --- a/ASTPlayground/ASTPlayground.dpr +++ b/ASTPlayground/ASTPlayground.dpr @@ -42,7 +42,9 @@ uses Myc.Data.Stream in '..\Src\Data\Myc.Data.Stream.pas', Myc.Fmx.AstEditor.Handlers.Pipes in '..\Src\AST\Myc.Fmx.AstEditor.Handlers.Pipes.pas', Demo.Finance in '..\Test\Demo.Finance.pas', - Myc.Ast.Script.Print in '..\Src\AST\Myc.Ast.Script.Print.pas'; + Myc.Ast.Script.Print in '..\Src\AST\Myc.Ast.Script.Print.pas', + Myc.Ast.Json.Schema in '..\Src\AST\Myc.Ast.Json.Schema.pas', + Myc.Ast.Attributes in 'Myc.Ast.Attributes.pas'; {$R *.res} diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj index 181de27..564b015 100644 --- a/ASTPlayground/ASTPlayground.dproj +++ b/ASTPlayground/ASTPlayground.dproj @@ -173,6 +173,8 @@ + + Base diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index f5d2f76..aa9549f 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -43,6 +43,7 @@ uses Myc.Ast.Environment, Myc.Ast.RTL, Myc.Ast.Compiler.Macros, + Myc.Ast.Json.Schema, // Editor Units Myc.Fmx.AstEditor, Demo.Finance, @@ -286,6 +287,8 @@ begin CompilerStageBox.BringToFront; ClearButtonClick(Self); + + Memo1.Lines.Text := TAstSchema.GenerateFullSystemPrompt; end; procedure TForm1.ShowVizualization; @@ -1223,24 +1226,29 @@ end; procedure TForm1.ToJSONButtonClick(Sender: TObject); var - jsonObj: TJSONObject; + jsonVal: TJSONValue; converter: IJsonAstConverter; begin Memo1.Lines.Clear; - if not Assigned(FCurrExec.Func) then + // Wir prüfen besser direkt, ob der AST da ist, den wir serialisieren wollen + if not Assigned(FCurrUnboundAst) then begin - Memo1.Lines.Add('No *compiled* AST has been generated yet. Click a test button first.'); + Memo1.Lines.Add('No AST has been generated yet. Click a test button first.'); exit; end; try converter := TJsonAstConverter.Create; - jsonObj := converter.Serialize(FCurrUnboundAst); + // Serialize liefert jetzt TJSONValue (Compact Array Format) + jsonVal := converter.Serialize(FCurrUnboundAst); try - Memo1.Lines.Text := jsonObj.Format(4); + if Assigned(jsonVal) then + Memo1.Lines.Text := jsonVal.Format(4) + else + Memo1.Lines.Text := 'null'; finally - jsonObj.Free; + jsonVal.Free; end; except on E: Exception do diff --git a/Src/AST/Myc.Ast.JSON.pas b/Src/AST/Myc.Ast.JSON.pas index d503567..0a090fc 100644 --- a/Src/AST/Myc.Ast.JSON.pas +++ b/Src/AST/Myc.Ast.JSON.pas @@ -1,3 +1,6 @@ +//================================================================================================== +//== FULL UNIT START: Myc.Ast.Json (from Myc.Ast.Json.pas) +//================================================================================================== unit Myc.Ast.Json; interface @@ -14,80 +17,57 @@ uses type IJsonAstConverter = interface - function Serialize(const RootNode: IAstNode): TJSONObject; - function Deserialize(const AJson: TJSONObject): IAstNode; + function Serialize(const RootNode: IAstNode): TJSONValue; + function Deserialize(const AJson: TJSONValue): IAstNode; end; - TJsonAstConverter = class(TAstVisitor, IJsonAstConverter) + // Compact JSON Converter: [Kind, Arg1, Arg2, ...] + 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; + // Helpers for compact data values + function DataValueToJson(const AValue: TDataValue): TJSONValue; + function JsonToDataValue(const AJson: TJSONValue): TDataValue; - // Deserialization Helpers - 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; - 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; - function JsonToTupleNode(const AObj: TJSONObject): ITupleNode; - function JsonToPipeNode(const AObj: TJSONObject): IPipeNode; + // Deserialization Dispatcher + function JsonToNode(const AJson: TJSONValue): IAstNode; + function JsonToTuple(const AArray: TJSONArray): ITupleNode; strict private - // Serialization Visitors - function VisitConstant(const Node: IAstNode): TJSONObject; - function VisitIdentifier(const Node: IAstNode): TJSONObject; - function VisitKeyword(const Node: IAstNode): TJSONObject; - function VisitTuple(const Node: IAstNode): TJSONObject; - function VisitRecordField(const Node: IAstNode): TJSONObject; - function VisitIfExpression(const Node: IAstNode): TJSONObject; - function VisitCondExpression(const Node: IAstNode): TJSONObject; - function VisitLambdaExpression(const Node: IAstNode): TJSONObject; - function VisitMacroDefinition(const Node: IAstNode): TJSONObject; - function VisitQuasiquote(const Node: IAstNode): TJSONObject; - function VisitUnquote(const Node: IAstNode): TJSONObject; - function VisitUnquoteSplicing(const Node: IAstNode): TJSONObject; - function VisitFunctionCall(const Node: IAstNode): TJSONObject; - function VisitMacroExpansionNode(const Node: IAstNode): TJSONObject; - function VisitRecurNode(const Node: IAstNode): TJSONObject; - function VisitBlockExpression(const Node: IAstNode): TJSONObject; - function VisitVariableDeclaration(const Node: IAstNode): TJSONObject; - function VisitAssignment(const Node: IAstNode): TJSONObject; - function VisitIndexer(const Node: IAstNode): TJSONObject; - function VisitMemberAccess(const Node: IAstNode): TJSONObject; - function VisitRecordLiteral(const Node: IAstNode): TJSONObject; - function VisitCreateSeries(const Node: IAstNode): TJSONObject; - function VisitAddSeriesItem(const Node: IAstNode): TJSONObject; - function VisitSeriesLength(const Node: IAstNode): TJSONObject; - function VisitNop(const Node: IAstNode): TJSONObject; - function VisitPipe(const Node: IAstNode): TJSONObject; + // Serialization Visitors (Array Builders) + function VisitConstant(const Node: IAstNode): TJSONValue; + function VisitIdentifier(const Node: IAstNode): TJSONValue; + function VisitKeyword(const Node: IAstNode): TJSONValue; + function VisitTuple(const Node: IAstNode): TJSONValue; + function VisitRecordField(const Node: IAstNode): TJSONValue; + function VisitIfExpression(const Node: IAstNode): TJSONValue; + function VisitCondExpression(const Node: IAstNode): TJSONValue; + function VisitLambdaExpression(const Node: IAstNode): TJSONValue; + function VisitMacroDefinition(const Node: IAstNode): TJSONValue; + function VisitQuasiquote(const Node: IAstNode): TJSONValue; + function VisitUnquote(const Node: IAstNode): TJSONValue; + function VisitUnquoteSplicing(const Node: IAstNode): TJSONValue; + function VisitFunctionCall(const Node: IAstNode): TJSONValue; + function VisitMacroExpansionNode(const Node: IAstNode): TJSONValue; + function VisitRecurNode(const Node: IAstNode): TJSONValue; + function VisitBlockExpression(const Node: IAstNode): TJSONValue; + function VisitVariableDeclaration(const Node: IAstNode): TJSONValue; + function VisitAssignment(const Node: IAstNode): TJSONValue; + function VisitIndexer(const Node: IAstNode): TJSONValue; + function VisitMemberAccess(const Node: IAstNode): TJSONValue; + function VisitRecordLiteral(const Node: IAstNode): TJSONValue; + function VisitCreateSeries(const Node: IAstNode): TJSONValue; + function VisitAddSeriesItem(const Node: IAstNode): TJSONValue; + function VisitSeriesLength(const Node: IAstNode): TJSONValue; + function VisitNop(const Node: IAstNode): TJSONValue; + function VisitPipe(const Node: IAstNode): TJSONValue; protected procedure SetupHandlers; override; public constructor Create; - function Serialize(const RootNode: IAstNode): TJSONObject; - function Deserialize(const AJson: TJSONObject): IAstNode; + function Serialize(const RootNode: IAstNode): TJSONValue; + function Deserialize(const AJson: TJSONValue): IAstNode; end; implementation @@ -133,652 +113,487 @@ begin Register(akPipe, VisitPipe); end; -function TJsonAstConverter.Serialize(const RootNode: IAstNode): TJSONObject; +function TJsonAstConverter.Serialize(const RootNode: IAstNode): TJSONValue; begin if not Assigned(RootNode) then - exit(nil); + exit(TJSONNull.Create); Result := Visit(RootNode); end; -function TJsonAstConverter.Deserialize(const AJson: TJSONObject): IAstNode; +function TJsonAstConverter.Deserialize(const AJson: TJSONValue): IAstNode; begin Result := JsonToNode(AJson); end; -procedure TJsonAstConverter.DataValueToJson(const AValue: TDataValue; const AParent: TJSONObject; const AName: string); -var - valObj, scalarObj: TJSONObject; -begin - valObj := TJSONObject.Create; - valObj.AddPair('Kind', TJSONString.Create(AValue.Kind.ToString)); +// --- Serialization (AST -> JSON Array) --- +function TJsonAstConverter.VisitIdentifier(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Id')); + arr.AddElement(TJSONString.Create(Node.AsIdentifier.Name)); + Result := arr; +end; + +function TJsonAstConverter.VisitKeyword(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Key')); + arr.AddElement(TJSONString.Create(Node.AsKeyword.Value.Name)); + Result := arr; +end; + +function TJsonAstConverter.VisitConstant(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Const')); + arr.AddElement(DataValueToJson(Node.AsConstant.Value)); + Result := arr; +end; + +function TJsonAstConverter.DataValueToJson(const AValue: TDataValue): TJSONValue; +begin 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.Ordinal: Result := TJSONNumber.Create(AValue.AsScalar.Value.AsInt64); + TScalar.TKind.Float: Result := TJSONNumber.Create(AValue.AsScalar.Value.AsDouble); + TScalar.TKind.Boolean: Result := TJSONBool.Create(AValue.AsScalar.Value.AsInt64 <> 0); TScalar.TKind.Keyword: - scalarObj.AddPair('Value', TJSONString.Create(TKeywordRegistry.GetName(AValue.AsScalar.Value.AsInt64))); + begin + var obj := TJSONObject.Create; + obj.AddPair('Keyword', TKeywordRegistry.GetName(AValue.AsScalar.Value.AsInt64)); + Result := obj; + end; + else + Result := TJSONNull.Create; end; - valObj.AddPair('Value', scalarObj); end; - vkText: valObj.AddPair('Value', TJSONString.Create(AValue.AsText)); - vkVoid:; + vkText: Result := TJSONString.Create(AValue.AsText); + vkVoid: Result := TJSONNull.Create; else - raise ENotSupportedException.Create('Unsupported TDataValue kind for constant serialization.'); + Result := TJSONString.Create(''); end; - - AParent.AddPair(AName, valObj); end; -function TJsonAstConverter.VisitConstant(const Node: IAstNode): TJSONObject; +function TJsonAstConverter.VisitTuple(const Node: IAstNode): TJSONValue; begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Constant')); - DataValueToJson(Node.AsConstant.Value, Result, 'Value'); + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Tuple')); + var elems := TJSONArray.Create; + for var item in Node.AsTuple.Elements do + elems.AddElement(Visit(item)); + arr.AddElement(elems); + Result := arr; end; -function TJsonAstConverter.VisitIdentifier(const Node: IAstNode): TJSONObject; -begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Identifier')); - Result.AddPair('Name', TJSONString.Create(Node.AsIdentifier.Name)); -end; - -function TJsonAstConverter.VisitKeyword(const Node: IAstNode): TJSONObject; -begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Keyword')); - Result.AddPair('Name', TJSONString.Create(Node.AsKeyword.Value.Name)); -end; - -function TJsonAstConverter.VisitTuple(const Node: IAstNode): TJSONObject; -var - T: ITupleNode; - elemsArray: TJSONArray; - i: Integer; - elements: TArray; -begin - T := Node.AsTuple; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Tuple')); - elemsArray := TJSONArray.Create; - - elements := T.Elements; - for i := 0 to High(elements) do - elemsArray.Add(Visit(elements[i])); - - Result.AddPair('Elements', elemsArray); -end; - -function TJsonAstConverter.VisitIfExpression(const Node: IAstNode): TJSONObject; -var - E: IIfExpressionNode; - elseObj: TJSONValue; -begin - E := Node.AsIfExpression; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('IfExpr')); - Result.AddPair('Condition', Visit(E.Condition)); - Result.AddPair('ThenBranch', Visit(E.ThenBranch)); - if Assigned(E.ElseBranch) then - elseObj := Visit(E.ElseBranch) - else - elseObj := TJSONNull.Create; - Result.AddPair('ElseBranch', elseObj); -end; - -function TJsonAstConverter.VisitCondExpression(const Node: IAstNode): TJSONObject; -var - E: ICondExpressionNode; - pairsArray: TJSONArray; - pairObj: TJSONObject; - elseObj: TJSONValue; - i: Integer; -begin - E := Node.AsCondExpression; - pairsArray := TJSONArray.Create; - for i := 0 to High(E.Pairs) do - begin - pairObj := TJSONObject.Create; - pairObj.AddPair('Condition', Visit(E.Pairs[i].Condition)); - pairObj.AddPair('Branch', Visit(E.Pairs[i].Branch)); - pairsArray.Add(pairObj); - end; - if Assigned(E.ElseBranch) then - elseObj := Visit(E.ElseBranch) - else - elseObj := TJSONNull.Create; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('CondExpr')); - Result.AddPair('Pairs', pairsArray); - Result.AddPair('ElseBranch', elseObj); -end; - -function TJsonAstConverter.VisitLambdaExpression(const Node: IAstNode): TJSONObject; -var - L: ILambdaExpressionNode; -begin - L := Node.AsLambdaExpression; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('LambdaExpr')); - Result.AddPair('Parameters', Visit(L.Parameters)); - Result.AddPair('Body', Visit(L.Body)); -end; - -function TJsonAstConverter.VisitMacroDefinition(const Node: IAstNode): TJSONObject; -var - M: IMacroDefinitionNode; -begin - M := Node.AsMacroDefinition; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('MacroDef')); - Result.AddPair('Name', Visit(M.Name)); - Result.AddPair('Parameters', Visit(M.Parameters)); - Result.AddPair('Body', Visit(M.Body)); -end; - -function TJsonAstConverter.VisitQuasiquote(const Node: IAstNode): TJSONObject; -begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Quasiquote')); - Result.AddPair('Expression', Visit(Node.AsQuasiquote.Expression)); -end; - -function TJsonAstConverter.VisitUnquote(const Node: IAstNode): TJSONObject; -begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Unquote')); - Result.AddPair('Expression', Visit(Node.AsUnquote.Expression)); -end; - -function TJsonAstConverter.VisitUnquoteSplicing(const Node: IAstNode): TJSONObject; -begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('UnquoteSplicing')); - Result.AddPair('Expression', Visit(Node.AsUnquoteSplicing.Expression)); -end; - -function TJsonAstConverter.VisitFunctionCall(const Node: IAstNode): TJSONObject; +function TJsonAstConverter.VisitFunctionCall(const Node: IAstNode): TJSONValue; var C: IFunctionCallNode; begin C := Node.AsFunctionCall; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('FunctionCall')); - Result.AddPair('Callee', Visit(C.Callee)); - Result.AddPair('Arguments', Visit(C.Arguments)); + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Call')); + arr.AddElement(Visit(C.Callee)); + arr.AddElement(Visit(C.Arguments)); // Arguments is a Tuple + Result := arr; end; -function TJsonAstConverter.VisitMacroExpansionNode(const Node: IAstNode): TJSONObject; +function TJsonAstConverter.VisitBlockExpression(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Block')); + arr.AddElement(Visit(Node.AsBlockExpression.Expressions)); // Tuple + Result := arr; +end; + +function TJsonAstConverter.VisitLambdaExpression(const Node: IAstNode): TJSONValue; var - M: IMacroExpansionNode; + L: ILambdaExpressionNode; begin - M := Node.AsMacroExpansion; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('MacroExpansion')); - Result.AddPair('Callee', Visit(M.CallNode.Callee)); - Result.AddPair('Arguments', Visit(M.CallNode.Arguments)); - Result.AddPair('ExpandedBody', Visit(M.ExpandedBody)); + L := Node.AsLambdaExpression; + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Fn')); + arr.AddElement(Visit(L.Parameters)); // Tuple + arr.AddElement(Visit(L.Body)); + Result := arr; end; -function TJsonAstConverter.VisitRecurNode(const Node: IAstNode): TJSONObject; +function TJsonAstConverter.VisitMacroDefinition(const Node: IAstNode): TJSONValue; +var + M: IMacroDefinitionNode; begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Recur')); - Result.AddPair('Arguments', Visit(Node.AsRecur.Arguments)); + M := Node.AsMacroDefinition; + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Macro')); + arr.AddElement(Visit(M.Name)); + arr.AddElement(Visit(M.Parameters)); + arr.AddElement(Visit(M.Body)); + Result := arr; end; -function TJsonAstConverter.VisitBlockExpression(const Node: IAstNode): TJSONObject; -begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Block')); - Result.AddPair('Expressions', Visit(Node.AsBlockExpression.Expressions)); -end; - -function TJsonAstConverter.VisitVariableDeclaration(const Node: IAstNode): TJSONObject; +function TJsonAstConverter.VisitVariableDeclaration(const Node: IAstNode): TJSONValue; var V: IVariableDeclarationNode; - initObj: TJSONValue; begin V := Node.AsVariableDeclaration; + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Var')); + arr.AddElement(Visit(V.Target)); if Assigned(V.Initializer) then - initObj := Visit(V.Initializer) + arr.AddElement(Visit(V.Initializer)) else - initObj := TJSONNull.Create; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('VarDecl')); - Result.AddPair('Identifier', Visit(V.Target)); - Result.AddPair('Initializer', initObj); + arr.AddElement(TJSONNull.Create); + Result := arr; end; -function TJsonAstConverter.VisitAssignment(const Node: IAstNode): TJSONObject; +function TJsonAstConverter.VisitAssignment(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Assign')); + arr.AddElement(Visit(Node.AsAssignment.Target)); + arr.AddElement(Visit(Node.AsAssignment.Value)); + Result := arr; +end; + +function TJsonAstConverter.VisitIfExpression(const Node: IAstNode): TJSONValue; var - A: IAssignmentNode; + E: IIfExpressionNode; begin - A := Node.AsAssignment; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Assignment')); - Result.AddPair('Identifier', Visit(A.Target)); - Result.AddPair('Value', Visit(A.Value)); -end; - -function TJsonAstConverter.VisitIndexer(const Node: IAstNode): TJSONObject; -var - I: IIndexerNode; -begin - I := Node.AsIndexer; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Indexer')); - Result.AddPair('Base', Visit(I.Base)); - Result.AddPair('Index', Visit(I.Index)); -end; - -function TJsonAstConverter.VisitMemberAccess(const Node: IAstNode): TJSONObject; -var - M: IMemberAccessNode; -begin - M := Node.AsMemberAccess; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('MemberAccess')); - Result.AddPair('Base', Visit(M.Base)); - Result.AddPair('Member', Visit(M.Member)); -end; - -function TJsonAstConverter.VisitRecordLiteral(const Node: IAstNode): TJSONObject; -begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('RecordLiteral')); - Result.AddPair('Fields', Visit(Node.AsRecordLiteral.Fields)); -end; - -function TJsonAstConverter.VisitRecordField(const Node: IAstNode): TJSONObject; -var - F: IRecordFieldNode; -begin - F := Node.AsRecordField; - Result := TJSONObject.Create; - Result.AddPair('Name', TJSONString.Create(F.Key.Value.Name)); - Result.AddPair('Value', Visit(F.Value)); -end; - -function TJsonAstConverter.VisitCreateSeries(const Node: IAstNode): TJSONObject; -begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('CreateSeries')); - // REFACTORED: Now serialize the DefinitionNode structure instead of a raw string - Result.AddPair('DefinitionNode', Visit(Node.AsCreateSeries.DefinitionNode)); -end; - -function TJsonAstConverter.VisitAddSeriesItem(const Node: IAstNode): TJSONObject; -var - A: IAddSeriesItemNode; - lookbackObj: TJSONValue; -begin - A := Node.AsAddSeriesItem; - if Assigned(A.Lookback) then - lookbackObj := Visit(A.Lookback) + E := Node.AsIfExpression; + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('If')); + arr.AddElement(Visit(E.Condition)); + arr.AddElement(Visit(E.ThenBranch)); + if Assigned(E.ElseBranch) then + arr.AddElement(Visit(E.ElseBranch)) else - lookbackObj := TJSONNull.Create; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('AddSeriesItem')); - Result.AddPair('Series', Visit(A.Series)); - Result.AddPair('Value', Visit(A.Value)); - Result.AddPair('Lookback', lookbackObj); + arr.AddElement(TJSONNull.Create); + Result := arr; end; -function TJsonAstConverter.VisitSeriesLength(const Node: IAstNode): TJSONObject; -begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('SeriesLength')); - Result.AddPair('Series', Visit(Node.AsSeriesLength.Series)); -end; - -function TJsonAstConverter.VisitNop(const Node: IAstNode): TJSONObject; -begin - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Nop')); -end; - -function TJsonAstConverter.VisitPipe(const Node: IAstNode): TJSONObject; +function TJsonAstConverter.VisitCondExpression(const Node: IAstNode): TJSONValue; var - P: IPipeNode; + E: ICondExpressionNode; + pairs: TJSONArray; begin - P := Node.AsPipe; - Result := TJSONObject.Create; - Result.AddPair('NodeType', TJSONString.Create('Pipe')); - Result.AddPair('Inputs', Visit(P.Inputs)); - Result.AddPair('Transformation', Visit(P.Transformation)); -end; - -{ Deserialization Handlers } - -function TJsonAstConverter.JsonToDataValue(const AObj: TJSONObject; const AName: string): TDataValue; -var - valObj, scalarObj: TJSONObject; - kindStr: string; -begin - valObj := AObj.GetValue(AName); - kindStr := valObj.GetValue('Kind'); - - if SameText(kindStr, 'Scalar') then + E := Node.AsCondExpression; + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Cond')); + pairs := TJSONArray.Create; + for var p in E.Pairs do begin - scalarObj := valObj.GetValue('Value'); - case TScalar.StringToKind(scalarObj.GetValue('Kind')) 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: Result := TScalar.FromKeyword(TKeywordRegistry.Intern(scalarObj.GetValue('Value'))); - end; + var pArr := TJSONArray.Create; + pArr.AddElement(Visit(p.Condition)); + pArr.AddElement(Visit(p.Branch)); + pairs.AddElement(pArr); + end; + arr.AddElement(pairs); + if Assigned(E.ElseBranch) then + arr.AddElement(Visit(E.ElseBranch)) + else + arr.AddElement(TJSONNull.Create); + Result := arr; +end; + +function TJsonAstConverter.VisitQuasiquote(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Quote')); + arr.AddElement(Visit(Node.AsQuasiquote.Expression)); + Result := arr; +end; + +function TJsonAstConverter.VisitUnquote(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Unquote')); + arr.AddElement(Visit(Node.AsUnquote.Expression)); + Result := arr; +end; + +function TJsonAstConverter.VisitUnquoteSplicing(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Splice')); + arr.AddElement(Visit(Node.AsUnquoteSplicing.Expression)); + Result := arr; +end; + +function TJsonAstConverter.VisitRecurNode(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Recur')); + arr.AddElement(Visit(Node.AsRecur.Arguments)); // Tuple + Result := arr; +end; + +function TJsonAstConverter.VisitIndexer(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Index')); + arr.AddElement(Visit(Node.AsIndexer.Base)); + arr.AddElement(Visit(Node.AsIndexer.Index)); + Result := arr; +end; + +function TJsonAstConverter.VisitMemberAccess(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Member')); + arr.AddElement(Visit(Node.AsMemberAccess.Base)); + arr.AddElement(Visit(Node.AsMemberAccess.Member)); + Result := arr; +end; + +function TJsonAstConverter.VisitRecordField(const Node: IAstNode): TJSONValue; +begin + // Helper for RecordLiteral + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Field')); + arr.AddElement(Visit(Node.AsRecordField.Key)); + arr.AddElement(Visit(Node.AsRecordField.Value)); + Result := arr; +end; + +function TJsonAstConverter.VisitRecordLiteral(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Record')); + arr.AddElement(Visit(Node.AsRecordLiteral.Fields)); // Tuple + Result := arr; +end; + +function TJsonAstConverter.VisitCreateSeries(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Series')); + arr.AddElement(Visit(Node.AsCreateSeries.DefinitionNode)); + Result := arr; +end; + +function TJsonAstConverter.VisitAddSeriesItem(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Add')); + arr.AddElement(Visit(Node.AsAddSeriesItem.Series)); + arr.AddElement(Visit(Node.AsAddSeriesItem.Value)); + if Assigned(Node.AsAddSeriesItem.Lookback) then + arr.AddElement(Visit(Node.AsAddSeriesItem.Lookback)) + else + arr.AddElement(TJSONNull.Create); + Result := arr; +end; + +function TJsonAstConverter.VisitSeriesLength(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Count')); + arr.AddElement(Visit(Node.AsSeriesLength.Series)); + Result := arr; +end; + +function TJsonAstConverter.VisitPipe(const Node: IAstNode): TJSONValue; +begin + var arr := TJSONArray.Create; + arr.AddElement(TJSONString.Create('Pipe')); + arr.AddElement(Visit(Node.AsPipe.Inputs)); + arr.AddElement(Visit(Node.AsPipe.Transformation)); + Result := arr; +end; + +function TJsonAstConverter.VisitNop(const Node: IAstNode): TJSONValue; +begin + Result := TJSONArray.Create; + TJSONArray(Result).AddElement(TJSONString.Create('Nop')); +end; + +function TJsonAstConverter.VisitMacroExpansionNode(const Node: IAstNode): TJSONValue; +begin + // Serialize original call + Result := Visit(Node.AsMacroExpansion.CallNode); +end; + +// --- Deserialization (JSON Array -> AST) --- + +function TJsonAstConverter.JsonToDataValue(const AJson: TJSONValue): TDataValue; +var + s: string; +begin + if AJson is TJSONNumber then + begin + var d := (AJson as TJSONNumber).AsDouble; + if Frac(d) = 0 then + Result := TScalar.FromInt64(Trunc(d)) + else + Result := TScalar.FromDouble(d); end - else if SameText(kindStr, 'Text') then - Result := valObj.GetValue('Value') - else if SameText(kindStr, 'Void') then - Result := TDataValue.Void + else if AJson is TJSONString then + Result := (AJson as TJSONString).Value + else if AJson is TJSONBool then + Result := TScalar.FromBoolean((AJson as TJSONBool).AsBoolean) + else if (AJson is TJSONObject) and (AJson as TJSONObject).TryGetValue('Keyword', s) then + Result := TScalar.FromKeyword(TKeywordRegistry.Intern(s)) else - raise ENotSupportedException.Create('Unsupported TDataValue kind.'); + Result := TDataValue.Void; end; -function TJsonAstConverter.JsonToNode(const AJson: TJSONValue; const ExpectedType: string): IAstNode; +function TJsonAstConverter.JsonToTuple(const AArray: TJSONArray): ITupleNode; var - obj: TJSONObject; - nodeType: string; -begin - if not (AJson is TJSONObject) then - raise EInvalidCast.Create('Expected JSON object.'); - 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 = '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) - 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 if nodeType = 'Tuple' then - Result := JsonToTupleNode(obj) - else if nodeType = 'Pipe' then - Result := JsonToPipeNode(obj) - else - raise ENotSupportedException.CreateFmt('Unsupported NodeType "%s".', [nodeType]); -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 - elseNode: IAstNode; -begin - if AObj.GetValue('ElseBranch') is TJSONNull then - elseNode := nil - else - elseNode := JsonToNode(AObj.GetValue('ElseBranch')); - Result := TAst.IfExpr(JsonToNode(AObj.GetValue('Condition')), JsonToNode(AObj.GetValue('ThenBranch')), elseNode); -end; - -function TJsonAstConverter.JsonToCondExprNode(const AObj: TJSONObject): ICondExpressionNode; -var - pairsArray: TJSONArray; - pairs: TArray; - i: Integer; - elseNode: IAstNode; -begin - pairsArray := AObj.GetValue('Pairs'); - SetLength(pairs, pairsArray.Count); - for i := 0 to pairsArray.Count - 1 do - pairs[i] := - TCondPair.Create( - JsonToNode((pairsArray.Items[i] as TJSONObject).GetValue('Condition')), - JsonToNode((pairsArray.Items[i] as TJSONObject).GetValue('Branch')) - ); - if AObj.GetValue('ElseBranch') is TJSONNull then - elseNode := nil - else - elseNode := JsonToNode(AObj.GetValue('ElseBranch')); - Result := TAst.CondExpr(pairs, elseNode); -end; - -function TJsonAstConverter.JsonToTernaryExprNode(const AObj: TJSONObject): IAstNode; -begin - Result := - TAst.TernaryExpr( - JsonToNode(AObj.GetValue('Condition')), - JsonToNode(AObj.GetValue('ThenBranch')), - JsonToNode(AObj.GetValue('ElseBranch')) - ); -end; - -function TJsonAstConverter.JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode; -var - identNodes: TList; - node: IAstNode; -begin - identNodes := TList.Create; - try - for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.Elements do - identNodes.Add(node.AsIdentifier); - Result := TAst.LambdaExpr(identNodes.ToArray, JsonToNode(AObj.GetValue('Body'))); - finally - identNodes.Free; - end; -end; - -function TJsonAstConverter.JsonToMacroDefNode(const AObj: TJSONObject): IMacroDefinitionNode; -var - identNodes: TList; - node: IAstNode; -begin - identNodes := TList.Create; - try - for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.Elements do - identNodes.Add(node.AsIdentifier); - Result := - TAst.MacroDef( - JsonToIdentifierNode(AObj.GetValue('Name') as TJSONObject), - identNodes.ToArray, - JsonToNode(AObj.GetValue('Body')) - ); - finally - identNodes.Free; - end; -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; -begin - Result := TAst.FunctionCall(JsonToNode(AObj.GetValue('Callee')), JsonToNode(AObj.GetValue('Arguments')).AsTuple.Elements); -end; - -function TJsonAstConverter.JsonToMacroExpansionNode(const AObj: TJSONObject): IMacroExpansionNode; -var - callee, expandedBody: IAstNode; - argsTuple: ITupleNode; -begin - callee := JsonToNode(AObj.GetValue('Callee')); - expandedBody := JsonToNode(AObj.GetValue('ExpandedBody')); - argsTuple := JsonToNode(AObj.GetValue('Arguments')).AsTuple; - Result := TAst.MacroExpansionNode(TAst.FunctionCall(callee, argsTuple.Elements), expandedBody); -end; - -function TJsonAstConverter.JsonToRecurNode(const AObj: TJSONObject): IRecurNode; -begin - Result := TAst.Recur(JsonToNode(AObj.GetValue('Arguments')).AsTuple.Elements); -end; - -function TJsonAstConverter.JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode; -begin - Result := TAst.Block(JsonToNode(AObj.GetValue('Expressions')).AsTuple.Elements); -end; - -function TJsonAstConverter.JsonToVarDeclNode(const AObj: TJSONObject): IVariableDeclarationNode; -var - initNode: IAstNode; -begin - if AObj.GetValue('Initializer') is TJSONNull then - initNode := nil - else - initNode := JsonToNode(AObj.GetValue('Initializer')); - Result := TAst.VarDecl(JsonToIdentifierNode(AObj.GetValue('Identifier') as TJSONObject), initNode); -end; - -function TJsonAstConverter.JsonToAssignmentNode(const AObj: TJSONObject): IAssignmentNode; -begin - Result := TAst.Assign(JsonToIdentifierNode(AObj.GetValue('Identifier') as TJSONObject), JsonToNode(AObj.GetValue('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).AsKeyword); -end; - -function TJsonAstConverter.JsonToRecordLiteralNode(const AObj: TJSONObject): IRecordLiteralNode; -var - fieldsArray: TJSONArray; - fields: TList; - i: Integer; -begin - fieldsArray := AObj.GetValue('Fields'); - fields := TList.Create; - try - for i := 0 to fieldsArray.Count - 1 do - begin - var fieldObj := fieldsArray.Items[i] as TJSONObject; - fields.Add(TAst.RecordField(TAst.Keyword(fieldObj.GetValue('Name')), JsonToNode(fieldObj.GetValue('Value')))); - end; - Result := TAst.RecordLiteral(fields.ToArray); - finally - fields.Free; - end; -end; - -function TJsonAstConverter.JsonToCreateSeriesNode(const AObj: TJSONObject): ICreateSeriesNode; -begin - // REFACTORED: Use the definition node from JSON - Result := TAst.CreateSeries(JsonToNode(AObj.GetValue('DefinitionNode'))); -end; - -function TJsonAstConverter.JsonToAddSeriesItemNode(const AObj: TJSONObject): IAddSeriesItemNode; -var - lookNode: IAstNode; -begin - if AObj.GetValue('Lookback') is TJSONNull then - lookNode := nil - else - lookNode := JsonToNode(AObj.GetValue('Lookback')); - Result := - TAst.AddSeriesItem(JsonToIdentifierNode(AObj.GetValue('Series') as TJSONObject), JsonToNode(AObj.GetValue('Value')), lookNode); -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.JsonToTupleNode(const AObj: TJSONObject): ITupleNode; -var - elemsArray: TJSONArray; elems: TList; i: Integer; begin - elemsArray := AObj.GetValue('Elements'); elems := TList.Create; try - for i := 0 to elemsArray.Count - 1 do - elems.Add(JsonToNode(elemsArray.Items[i])); + for i := 0 to AArray.Count - 1 do + elems.Add(JsonToNode(AArray.Items[i])); Result := TAst.Tuple(elems.ToArray); finally elems.Free; end; end; -function TJsonAstConverter.JsonToPipeNode(const AObj: TJSONObject): IPipeNode; +function TJsonAstConverter.JsonToNode(const AJson: TJSONValue): IAstNode; +var + arr: TJSONArray; + kind: string; begin - Result := TAst.Pipe(JsonToNode(AObj.GetValue('Inputs')).AsTuple, JsonToNode(AObj.GetValue('Transformation')).AsLambdaExpression); + if AJson is TJSONNull then + exit(nil); + + if not (AJson is TJSONArray) then + raise EInvalidCast.Create('AST Node must be a JSON Array'); + + arr := AJson as TJSONArray; + if arr.Count = 0 then + raise EInvalidCast.Create('Empty node array'); + + kind := arr.Items[0].Value; + + if kind = 'Id' then + Exit(TAst.Identifier(arr.Items[1].Value)); + if kind = 'Key' then + Exit(TAst.Keyword(arr.Items[1].Value)); + if kind = 'Const' then + Exit(TAst.Constant(JsonToDataValue(arr.Items[1]))); + if kind = 'Tuple' then + Exit(JsonToTuple(arr.Items[1] as TJSONArray)); + if kind = 'Block' then + Exit(TAst.Block(JsonToTuple(arr.Items[1] as TJSONArray).Elements)); + if kind = 'Nop' then + Exit(TAst.Nop); + + if kind = 'Call' then + Exit(TAst.FunctionCall(JsonToNode(arr.Items[1]), JsonToTuple(arr.Items[2] as TJSONArray).Elements)); + + if kind = 'Fn' then + // Pass TIdentities.Structural as Identity to satisfy the overload requiring (Identity, Tuple, Body, ...) + Exit(TAst.LambdaExpr(TIdentities.Structural, JsonToTuple(arr.Items[1] as TJSONArray), JsonToNode(arr.Items[2]))); + + if kind = 'Macro' then + // Pass TIdentities.Structural as Identity + Exit( + TAst.MacroDef( + TIdentities.Structural, + JsonToNode(arr.Items[1]).AsIdentifier, + JsonToTuple(arr.Items[2] as TJSONArray), + JsonToNode(arr.Items[3]) + ) + ); + + if kind = 'Var' then + begin + var init: IAstNode := nil; + if arr.Count > 2 then + init := JsonToNode(arr.Items[2]); + Exit(TAst.VarDecl(JsonToNode(arr.Items[1]), init)); + end; + + if kind = 'Assign' then + Exit(TAst.Assign(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]))); + + if kind = 'If' then + begin + var el: IAstNode := nil; + if arr.Count > 3 then + el := JsonToNode(arr.Items[3]); + Exit(TAst.IfExpr(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]), el)); + end; + + if kind = 'Cond' then + begin + var pairArr := arr.Items[1] as TJSONArray; + var pairs: TArray; + SetLength(pairs, pairArr.Count); + for var i := 0 to pairArr.Count - 1 do + begin + var p := pairArr.Items[i] as TJSONArray; + pairs[i] := TCondPair.Create(JsonToNode(p.Items[0]), JsonToNode(p.Items[1])); + end; + var el: IAstNode := nil; + if arr.Count > 2 then + el := JsonToNode(arr.Items[2]); + Exit(TAst.CondExpr(pairs, el)); + end; + + if kind = 'Quote' then + Exit(TAst.Quasiquote(JsonToNode(arr.Items[1]))); + if kind = 'Unquote' then + Exit(TAst.Unquote(JsonToNode(arr.Items[1]))); + if kind = 'Splice' then + Exit(TAst.UnquoteSplicing(JsonToNode(arr.Items[1]).AsQuasiquote)); + if kind = 'Recur' then + Exit(TAst.Recur(JsonToTuple(arr.Items[1] as TJSONArray).Elements)); + + if kind = 'Index' then + Exit(TAst.Indexer(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]))); + if kind = 'Member' then + Exit(TAst.MemberAccess(JsonToNode(arr.Items[1]), JsonToNode(arr.Items[2]).AsKeyword)); + + if kind = 'Record' then + begin + var fieldsTuple := JsonToTuple(arr.Items[1] as TJSONArray); + var recFields: TList := TList.Create; + try + for var elem in fieldsTuple.Elements do + begin + if elem.Kind = akRecordField then + recFields.Add(elem.AsRecordField) + else + raise EInvalidCast.Create('Expected RecordField'); + end; + Exit(TAst.RecordLiteral(recFields.ToArray)); + finally + recFields.Free; + end; + end; + + // Explicit RecordField deserializer support + if kind = 'Field' then + Exit(TAst.RecordField(JsonToNode(arr.Items[1]).AsKeyword, JsonToNode(arr.Items[2]))); + + if kind = 'Series' then + Exit(TAst.CreateSeries(JsonToNode(arr.Items[1]))); + + if kind = 'Add' then + begin + var lb: IAstNode := nil; + if arr.Count > 3 then + lb := JsonToNode(arr.Items[3]); + Exit(TAst.AddSeriesItem(JsonToNode(arr.Items[1]).AsIdentifier, JsonToNode(arr.Items[2]), lb)); + end; + + if kind = 'Count' then + Exit(TAst.SeriesLength(JsonToNode(arr.Items[1]).AsIdentifier)); + + if kind = 'Pipe' then + Exit(TAst.Pipe(JsonToTuple(arr.Items[1] as TJSONArray), JsonToNode(arr.Items[2]).AsLambdaExpression)); + + raise ENotSupportedException.Create('Unknown JSON AST Kind: ' + kind); end; end. + //================================================================================================== +//== FULL UNIT END: Myc.Ast.Json +//================================================================================================== diff --git a/Src/AST/Myc.Ast.Json.Schema.pas b/Src/AST/Myc.Ast.Json.Schema.pas new file mode 100644 index 0000000..1c9ce18 --- /dev/null +++ b/Src/AST/Myc.Ast.Json.Schema.pas @@ -0,0 +1,219 @@ +unit Myc.Ast.Json.Schema; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Collections, + System.Generics.Defaults, + System.Rtti, + System.TypInfo, + Myc.Ast.Nodes, + Myc.Ast.Attributes; + +type + TAstSchema = class + public + // Generates the TypeScript definition by scanning classes via RTTI + class function GenerateTypeScriptDefinition: string; + + // Static rules for the format + class function GenerateGenerationRules: string; + + // Combined prompt + class function GenerateFullSystemPrompt: string; + end; + +implementation + +type + // Helper structure to collect fields per type + // Moved here to avoid scope issues with generics + TFieldInfo = record + Idx: Integer; + Def: string; + end; + +{ TAstSchema } + +class function TAstSchema.GenerateTypeScriptDefinition: string; +var + ctx: TRttiContext; + types: TArray; + typ: TRttiType; + attr: TCustomAttribute; + fieldAttr: AstFieldAttribute; + nodeDefs: TDictionary>; + unionTypes: TList; + fieldsList: TList; + sb: TStringBuilder; + tag: string; + fieldInfo: TFieldInfo; + f: TRttiField; + i: Integer; + + function GetTsType(Kind: TFieldKind): string; + begin + case Kind of + fkNode: Result := 'AstNode'; + fkTuple: Result := 'Tuple'; + fkString: Result := 'string'; + fkValue: Result := 'number | string | boolean'; + fkNullableNode: Result := 'AstNode | null'; + fkArrayOfPairs: Result := 'Array<[AstNode, AstNode]>'; + else + Result := 'any'; + end; + end; + +begin + ctx := TRttiContext.Create; + nodeDefs := TDictionary>.Create; + unionTypes := TList.Create; + sb := TStringBuilder.Create; + + try + // 1. Scan all types in the application context + types := ctx.GetTypes; + + for typ in types do + begin + if not typ.IsInstance then + Continue; + + // Check for [AstTag] attribute on the class + tag := ''; + for attr in typ.GetAttributes do + if attr is AstTagAttribute then + begin + tag := AstTagAttribute(attr).Tag; + Break; + end; + + if tag = '' then + Continue; + + if not nodeDefs.ContainsKey(tag) then + begin + nodeDefs.Add(tag, TList.Create); + unionTypes.Add(tag); + end; + + fieldsList := nodeDefs[tag]; + + // Scan Fields (Variables) of the class + for f in typ.AsInstance.GetFields do + begin + for attr in f.GetAttributes do + if attr is AstFieldAttribute then + begin + fieldAttr := AstFieldAttribute(attr); + + fieldInfo.Idx := fieldAttr.Index; + fieldInfo.Def := GetTsType(fieldAttr.Kind) + ' /*' + fieldAttr.Name + '*/'; + + fieldsList.Add(fieldInfo); + end; + end; + end; + + // 2. Build the output + sb.AppendLine('// AST Schema Definition (Auto-Generated via RTTI)'); + sb.AppendLine('// Format: Compact JSON Arrays [Tag, Arg1, Arg2, ...]'); + sb.AppendLine; + + unionTypes.Sort; + + // Union Type Definition + sb.AppendLine('type AstNode = '); + for i := 0 to unionTypes.Count - 1 do + begin + sb.Append(' | ' + unionTypes[i]); + if i < unionTypes.Count - 1 then + sb.AppendLine; + end; + sb.AppendLine(';'); + sb.AppendLine; + + sb.AppendLine('type Tuple = ["Tuple", AstNode[]];'); + sb.AppendLine; + + // Node Definitions + for tag in unionTypes do + begin + // Tuple is special base type + if tag = 'Tuple' then + Continue; + + fieldsList := nodeDefs[tag]; + + // Sort fields by Index to ensure correct JSON array order + fieldsList.Sort(TComparer.Construct(function(const L, R: TFieldInfo): Integer begin Result := L.Idx - R.Idx; end)); + + sb.Append('type ' + tag + ' = ["' + tag + '"'); + + for fieldInfo in fieldsList do + sb.Append(', ' + fieldInfo.Def); + + sb.AppendLine('];'); + end; + + Result := sb.ToString; + + finally + // Clean up lists in dictionary + for tag in nodeDefs.Keys do + nodeDefs[tag].Free; + + nodeDefs.Free; + unionTypes.Free; + sb.Free; + ctx.Free; + end; +end; + +class function TAstSchema.GenerateGenerationRules: string; +var + sb: TStringBuilder; +begin + // These rules describe the PROTOCOL (Compact Arrays), not the content. + // They remain static unless you change TJsonAstConverter logic significantly. + sb := TStringBuilder.Create; + try + sb.AppendLine('**âš ï¸ CRITICAL GENERATION RULES:**'); + sb.AppendLine; + sb.AppendLine('1. **NO JSON OBJECTS:** Do not use `{ "key": "value" }`. Every node MUST be a JSON Array `["Tag", Arg1, Arg2]`.'); + sb.AppendLine( + '2. **EXPLICIT TUPLES:** Whenever a field in the schema is defined as `Tuple`, you MUST wrap the list in a `["Tuple", [...]]` node.' + ); + sb.AppendLine( + '3. **EXPLICIT NULLS:** Optional fields (marked as `| null`) MUST be explicit `null` if not present. Do not omit them.' + ); + sb.AppendLine('4. **STRICT ORDER:** The order of elements in the array is fixed defined by the schema. Never swap arguments.'); + + Result := sb.ToString; + finally + sb.Free; + end; +end; + +class function TAstSchema.GenerateFullSystemPrompt: string; +begin + // Combine everything + Result := + 'You are a compiler frontend for the "Myc" scripting language.' + + sLineBreak + + 'Your task is to generate a valid Abstract Syntax Tree (AST) in a specific Compact JSON format.' + + sLineBreak + + sLineBreak + + '### 1. AST Schema (TypeScript Definition)' + + sLineBreak + + GenerateTypeScriptDefinition + + sLineBreak + + '### 2. Constraints & Rules' + + sLineBreak + + GenerateGenerationRules; +end; + +end. diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index dde9248..d476668 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -1,3 +1,6 @@ +//================================================================================================== +//== FULL UNIT START: Myc.Ast.Nodes (from Myc.Ast.Nodes.pas) +//================================================================================================== unit Myc.Ast.Nodes; interface @@ -10,7 +13,8 @@ uses Myc.Data.Keyword, Myc.Ast.Scope, Myc.Ast.Types, - Myc.Ast.Identities; + Myc.Ast.Identities, + Myc.Ast.Attributes; // Hinzugefügt für [AstTag] und [AstField] type // --- Forward Declarations --- @@ -197,7 +201,6 @@ type function AsRecur: IRecurNode; function AsNop: INopNode; - // Pipes function AsPipe: IPipeNode; property IsTyped: Boolean read GetIsTyped; @@ -528,9 +531,12 @@ type property StaticType: IStaticType read GetStaticType; end; + [AstTag('Field')] TRecordFieldNode = class(TAstNode, IRecordFieldNode) private + [AstField(0, 'key', fkNode)] FKey: IKeywordNode; + [AstField(1, 'value', fkNode)] FValue: IAstNode; function GetKey: IKeywordNode; function GetValue: IAstNode; @@ -543,8 +549,10 @@ type // --- Core Nodes Implementations --- + [AstTag('Const')] TConstantNode = class(TAstTypedNode, IConstantNode) private + [AstField(0, 'value', fkValue)] FConstIdentity: IConstantIdentity; // Typed reference for fast access function GetValue: TDataValue; protected @@ -554,8 +562,10 @@ type function AsConstant: IConstantNode; override; end; + [AstTag('Id')] TIdentifierNode = class(TAstTypedNode, IIdentifierNode) private + [AstField(0, 'name', fkString)] FNamedIdentity: INamedIdentity; // Typed reference for fast access FAddress: TResolvedAddress; function GetAddress: TResolvedAddress; @@ -567,8 +577,10 @@ type function AsIdentifier: IIdentifierNode; override; end; + [AstTag('Key')] TKeywordNode = class(TAstTypedNode, IKeywordNode) private + [AstField(0, 'name', fkString)] FKeywordIdentity: IKeywordIdentity; function GetValue: IKeyword; protected @@ -578,8 +590,10 @@ type function AsKeyword: IKeywordNode; override; end; + [AstTag('Tuple')] TTupleNode = class(TAstTypedNode, ITupleNode) private + [AstField(0, 'elements', fkNode)] // Represents array FElements: TArray; function GetElements: TArray; protected @@ -589,8 +603,10 @@ type function AsTuple: ITupleNode; override; end; + [AstTag('Series')] TCreateSeriesNode = class(TAstTypedNode, ICreateSeriesNode) private + [AstField(0, 'definition', fkNode)] FDefinitionNode: IAstNode; FRecordDefinition: IScalarRecordDefinition; function GetDefinitionNode: IAstNode; @@ -609,9 +625,15 @@ type // --- Structural Nodes (Identity only for location) --- + [AstTag('If')] TIfExpressionNode = class(TAstTypedNode, IIfExpressionNode) private - FCondition, FThenBranch, FElseBranch: IAstNode; + [AstField(0, 'cond', fkNode)] + FCondition: IAstNode; + [AstField(1, 'then', fkNode)] + FThenBranch: IAstNode; + [AstField(2, 'else', fkNullableNode)] + FElseBranch: IAstNode; function GetCondition: IAstNode; function GetThenBranch: IAstNode; function GetElseBranch: IAstNode; @@ -626,9 +648,12 @@ type function AsIfExpression: IIfExpressionNode; override; end; + [AstTag('Cond')] TCondExpressionNode = class(TAstTypedNode, ICondExpressionNode) private + [AstField(0, 'pairs', fkArrayOfPairs)] FPairs: TArray; + [AstField(1, 'else', fkNullableNode)] FElseBranch: IAstNode; function GetPairs: TArray; function GetElseBranch: IAstNode; @@ -644,9 +669,12 @@ type function AsCondExpression: ICondExpressionNode; override; end; + [AstTag('Fn')] TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode, IFunctionDefinition) private + [AstField(0, 'params', fkTuple)] FParameters: ITupleNode; + [AstField(1, 'body', fkNode)] FBody: IAstNode; FLayout: IScopeLayout; FDescriptor: IScopeDescriptor; @@ -675,9 +703,12 @@ type function AsLambdaExpression: ILambdaExpressionNode; override; end; + [AstTag('Call')] TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode) private + [AstField(0, 'callee', fkNode)] FCallee: IAstNode; + [AstField(1, 'args', fkTuple)] FArguments: ITupleNode; FIsTailCall: Boolean; FStaticTarget: TDataValue.TFunc; @@ -702,10 +733,14 @@ type function AsFunctionCall: IFunctionCallNode; override; end; + [AstTag('Macro')] TMacroDefinitionNode = class(TAstTypedNode, IMacroDefinitionNode) private + [AstField(0, 'name', fkNode)] FName: IIdentifierNode; + [AstField(1, 'params', fkTuple)] FParameters: ITupleNode; + [AstField(2, 'body', fkNode)] FBody: IAstNode; function GetName: IIdentifierNode; function GetParameters: ITupleNode; @@ -735,8 +770,10 @@ type function AsMacroExpansion: IMacroExpansionNode; override; end; + [AstTag('Quote')] TQuasiquoteNode = class(TAstNode, IQuasiquoteNode) private + [AstField(0, 'expr', fkNode)] FExpression: IAstNode; function GetExpression: IAstNode; protected @@ -746,8 +783,10 @@ type function AsQuasiquote: IQuasiquoteNode; override; end; + [AstTag('Unquote')] TUnquoteNode = class(TAstNode, IUnquoteNode) private + [AstField(0, 'expr', fkNode)] FExpression: IAstNode; function GetExpression: IAstNode; protected @@ -757,8 +796,10 @@ type function AsUnquote: IUnquoteNode; override; end; + [AstTag('Splice')] TUnquoteSplicingNode = class(TAstNode, IUnquoteSplicingNode) private + [AstField(0, 'expr', fkNode)] FExpression: IQuasiquoteNode; function GetExpression: IQuasiquoteNode; protected @@ -768,8 +809,10 @@ type function AsUnquoteSplicing: IUnquoteSplicingNode; override; end; + [AstTag('Recur')] TRecurNode = class(TAstTypedNode, IRecurNode) private + [AstField(0, 'args', fkTuple)] FArguments: ITupleNode; function GetArguments: ITupleNode; protected @@ -779,8 +822,10 @@ type function AsRecur: IRecurNode; override; end; + [AstTag('Block')] TBlockExpressionNode = class(TAstTypedNode, IBlockExpressionNode) private + [AstField(0, 'expressions', fkTuple)] FExpressions: ITupleNode; function GetExpressions: ITupleNode; protected @@ -790,9 +835,13 @@ type function AsBlockExpression: IBlockExpressionNode; override; end; + [AstTag('Var')] TVariableDeclarationNode = class(TAstTypedNode, IVariableDeclarationNode) private - FTarget, FInitializer: IAstNode; + [AstField(0, 'target', fkNode)] + FTarget: IAstNode; + [AstField(1, 'init', fkNullableNode)] + FInitializer: IAstNode; FIsBoxed: Boolean; function GetTarget: IAstNode; function GetInitializer: IAstNode; @@ -810,9 +859,13 @@ type function AsVariableDeclaration: IVariableDeclarationNode; override; end; + [AstTag('Assign')] TAssignmentNode = class(TAstTypedNode, IAssignmentNode) private - FTarget, FValue: IAstNode; + [AstField(0, 'target', fkNode)] + FTarget: IAstNode; + [AstField(1, 'value', fkNode)] + FValue: IAstNode; function GetTarget: IAstNode; function GetValue: IAstNode; protected @@ -822,9 +875,13 @@ type function AsAssignment: IAssignmentNode; override; end; + [AstTag('Index')] TIndexerNode = class(TAstTypedNode, IIndexerNode) private - FBase, FIndex: IAstNode; + [AstField(0, 'base', fkNode)] + FBase: IAstNode; + [AstField(1, 'index', fkNode)] + FIndex: IAstNode; function GetBase: IAstNode; function GetIndex: IAstNode; protected @@ -834,9 +891,12 @@ type function AsIndexer: IIndexerNode; override; end; + [AstTag('Member')] TMemberAccessNode = class(TAstTypedNode, IMemberAccessNode) private + [AstField(0, 'base', fkNode)] FBase: IAstNode; + [AstField(1, 'member', fkNode)] FMember: IKeywordNode; function GetBase: IAstNode; function GetMember: IKeywordNode; @@ -852,8 +912,10 @@ type function AsMemberAccess: IMemberAccessNode; override; end; + [AstTag('Record')] TRecordLiteralNode = class(TAstTypedNode, IRecordLiteralNode) private + [AstField(0, 'fields', fkTuple)] FFields: ITupleNode; FScalarDef: IScalarRecordDefinition; FGenericDef: IGenericRecordDefinition; @@ -873,10 +935,15 @@ type function AsRecordLiteral: IRecordLiteralNode; override; end; + [AstTag('Add')] TAddSeriesItemNode = class(TAstTypedNode, IAddSeriesItemNode) private + [AstField(0, 'series', fkNode)] FSeries: IIdentifierNode; - FValue, FLookback: IAstNode; + [AstField(1, 'value', fkNode)] + FValue: IAstNode; + [AstField(2, 'lookback', fkNullableNode)] + FLookback: IAstNode; function GetSeries: IIdentifierNode; function GetValue: IAstNode; function GetLookback: IAstNode; @@ -892,8 +959,10 @@ type function AsAddSeriesItem: IAddSeriesItemNode; override; end; + [AstTag('Count')] TSeriesLengthNode = class(TAstTypedNode, ISeriesLengthNode) private + [AstField(0, 'series', fkNode)] FSeries: IIdentifierNode; function GetSeries: IIdentifierNode; protected @@ -903,6 +972,7 @@ type function AsSeriesLength: ISeriesLengthNode; override; end; + [AstTag('Nop')] TNopNode = class(TAstTypedNode, INopNode) protected function GetKind: TAstNodeKind; override; @@ -911,9 +981,12 @@ type function AsNop: INopNode; override; end; + [AstTag('Pipe')] TPipeNode = class(TAstTypedNode, IPipeNode) private + [AstField(0, 'inputs', fkTuple)] FInputs: ITupleNode; + [AstField(1, 'transform', fkNode)] FTransformation: ILambdaExpressionNode; function GetInputs: ITupleNode; function GetTransformation: ILambdaExpressionNode; @@ -933,6 +1006,7 @@ implementation uses System.Classes, + System.TypInfo, System.Rtti, System.StrUtils; @@ -949,7 +1023,7 @@ end; function TAstNodeKindHelper.ToString: string; begin - Result := TRttiEnumerationType.GetName(Self); + Result := GetEnumName(TypeInfo(TAstNodeKind), Ord(Self)); Assert(StartsText('ak', Result)); Result := Result.Substring(2); end; @@ -2055,3 +2129,6 @@ begin end; end. + //================================================================================================== +//== FULL UNIT END: Myc.Ast.Nodes +//==================================================================================================