//================================================================================================== //== FULL UNIT START: Myc.Ast.Json (from Myc.Ast.Json.pas) //================================================================================================== unit Myc.Ast.Json; interface uses System.SysUtils, System.Generics.Collections, System.JSON, Myc.Data.Scalar, Myc.Data.Value, Myc.Ast, Myc.Ast.Visitor, Myc.Ast.Nodes; type IJsonAstConverter = interface function Serialize(const RootNode: IAstNode): TJSONValue; function Deserialize(const AJson: TJSONValue): IAstNode; end; // Compact JSON Converter: [Kind, Arg1, Arg2, ...] TJsonAstConverter = class(TAstVisitor, IJsonAstConverter) private // Helpers for compact data values function DataValueToJson(const AValue: TDataValue): TJSONValue; function JsonToDataValue(const AJson: TJSONValue): TDataValue; // Deserialization Dispatcher function JsonToNode(const AJson: TJSONValue): IAstNode; function JsonToTuple(const AArray: TJSONArray): ITupleNode; strict private // 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): TJSONValue; function Deserialize(const AJson: TJSONValue): IAstNode; end; implementation uses Myc.Data.Keyword, Myc.Ast.Identities; { TJsonAstConverter } constructor TJsonAstConverter.Create; begin inherited; end; procedure TJsonAstConverter.SetupHandlers; begin Register(akConstant, VisitConstant); Register(akIdentifier, VisitIdentifier); Register(akKeyword, VisitKeyword); Register(akTuple, VisitTuple); Register(akRecordField, VisitRecordField); Register(akIfExpression, VisitIfExpression); Register(akCondExpression, VisitCondExpression); Register(akLambdaExpression, VisitLambdaExpression); Register(akFunctionCall, VisitFunctionCall); Register(akMacroExpansion, VisitMacroExpansionNode); Register(akBlockExpression, VisitBlockExpression); Register(akVariableDeclaration, VisitVariableDeclaration); Register(akAssignment, VisitAssignment); Register(akMacroDefinition, VisitMacroDefinition); Register(akQuasiquote, VisitQuasiquote); Register(akUnquote, VisitUnquote); Register(akUnquoteSplicing, VisitUnquoteSplicing); Register(akIndexer, VisitIndexer); Register(akMemberAccess, VisitMemberAccess); Register(akRecordLiteral, VisitRecordLiteral); Register(akCreateSeries, VisitCreateSeries); Register(akAddSeriesItem, VisitAddSeriesItem); Register(akSeriesLength, VisitSeriesLength); Register(akRecur, VisitRecurNode); Register(akNop, VisitNop); Register(akPipe, VisitPipe); end; function TJsonAstConverter.Serialize(const RootNode: IAstNode): TJSONValue; begin if not Assigned(RootNode) then exit(TJSONNull.Create); Result := Visit(RootNode); end; function TJsonAstConverter.Deserialize(const AJson: TJSONValue): IAstNode; begin Result := JsonToNode(AJson); end; // --- 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 case AValue.AsScalar.Kind of 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: begin var obj := TJSONObject.Create; obj.AddPair('Keyword', TKeywordRegistry.GetName(AValue.AsScalar.Value.AsInt64)); Result := obj; end; else Result := TJSONNull.Create; end; end; vkText: Result := TJSONString.Create(AValue.AsText); vkVoid: Result := TJSONNull.Create; else Result := TJSONString.Create(''); end; end; function TJsonAstConverter.VisitTuple(const Node: IAstNode): TJSONValue; begin 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.VisitFunctionCall(const Node: IAstNode): TJSONValue; var C: IFunctionCallNode; begin C := Node.AsFunctionCall; 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.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 L: ILambdaExpressionNode; begin 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.VisitMacroDefinition(const Node: IAstNode): TJSONValue; var M: IMacroDefinitionNode; begin 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.VisitVariableDeclaration(const Node: IAstNode): TJSONValue; var V: IVariableDeclarationNode; begin V := Node.AsVariableDeclaration; var arr := TJSONArray.Create; arr.AddElement(TJSONString.Create('Var')); arr.AddElement(Visit(V.Target)); if Assigned(V.Initializer) then arr.AddElement(Visit(V.Initializer)) else arr.AddElement(TJSONNull.Create); Result := arr; end; 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 E: IIfExpressionNode; begin 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 arr.AddElement(TJSONNull.Create); Result := arr; end; function TJsonAstConverter.VisitCondExpression(const Node: IAstNode): TJSONValue; var E: ICondExpressionNode; pairs: TJSONArray; begin E := Node.AsCondExpression; var arr := TJSONArray.Create; arr.AddElement(TJSONString.Create('Cond')); pairs := TJSONArray.Create; for var p in E.Pairs do begin 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 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 Result := TDataValue.Void; end; function TJsonAstConverter.JsonToTuple(const AArray: TJSONArray): ITupleNode; var elems: TList; i: Integer; begin elems := TList.Create; try 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.JsonToNode(const AJson: TJSONValue): IAstNode; var arr: TJSONArray; kind: string; begin 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 //==================================================================================================