123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381open!Basetype'at={sexp_of:'a->Sexp.t;of_sexp:Sexp.t->'a}letcreate_m(typea)(moduleM:Sexpable.Swithtypet=a)={sexp_of=M.sexp_of_t;of_sexp=M.t_of_sexp};;letmap_unmap{sexp_of;of_sexp}~to_~of_={sexp_of=Fn.composesexp_ofof_;of_sexp=Fn.composeto_of_sexp};;moduleExport=structletserialization_int=create_m(moduleInt)letserialization_list{sexp_of;of_sexp}={sexp_of=List.sexp_of_tsexp_of;of_sexp=List.t_of_sexpof_sexp};;endmoduleNamed=structtypet=Namedofstringend(* Because sexp serialization is an intensional property of a type -- syntactic details
like label names matter -- we cannot simply use the [Of_basic] functor. Instead, we use
the [Types] and [Fold] functors from [Ppx_derive_at_runtime_lib].
When constructing a function using a [Fold] functor, we do the folding outside [fun]
or [function]. This way we traverse the type only once. *)moduleDerive=structopenPpx_derive_at_runtime_lib(* We must define the derived value type and attribute types we are using. *)moduleValue=structtypenonrec'at='attype_attribute=Named.ttype(_,_)label_attribute=Named.ttype(_,_)row_attribute=Named.ttype(_,_)constructor_attribute=Named.tend(* The [Types] functors gives us the GADT binary trees for tuples, records, variants,
and polymorphic variants. *)moduleTypes=Types(Value)openTypes(* We derive [sexp_of] and [of_sexp] separately, and combine them into a [t] later.
This makes for easier to read code than trying to derive both at once. *)(* How to derive [sexp_of]: *)moduleSexp_of=structmoduleProduct_acc=struct(* For product types, our accumulator function conses onto a list of sexps. *)type('whole,_)t='whole->Sexp.tlist->Sexp.tlistendmoduleFold_tuple=Tuple.Fold(Product_acc)(* Conses tuple elements onto a sexp. Used for tuple values, and for inline tuples in
variant constructor arguments. *)lettuple_sexps=Fold_tuple.fold~leaf:{on_leaf=(fun{index=_;value;access}xsexps->value.sexp_of(accessx)::sexps)}~node:{on_node=(funleftrightxsexps->leftx(rightxsexps))};;lettuple(Tuple.Troot)=letsexps_of=tuple_sexpsroot.treeinfunx->Sexp.List(sexps_ofx[]);;moduleFold_record=Record.Fold(Product_acc)(* Conses record labels, wrapped with their labels, onto a sexp. Used for record
values, and for inline records in variant constructor arguments. *)letrecord_sexps=Fold_record.fold~leaf:{on_leaf=(fun{name;value;attribute;access}xsexps->letname=matchattributewith|Some(Namedname)->name|None->nameinList[Atomname;value.sexp_of(accessx)]::sexps)}~node:{on_node=(funleftrightxsexps->leftx(rightxsexps))};;letrecord(Record.Troot)=letsexps_of=record_sexpsroot.treeinfunx->Sexp.List(sexps_ofx[]);;moduleSum_acc=struct(* For sum types, our accumulator function dispatches on [Either.t]s. *)type(_,'eithers)t='eithers->Sexp.tendmoduleFold_variant=Variant.Fold(Sum_acc)(* Dispatches on variant structure to construct a sexp. Uses [tuple_sexps] and
[record_sexps] from above. [on_leaf] callbacks usually need type annotations to
guarantee they are polymorphic in the ['part] type parameters. *)letvariant(Variant.Troot)=letsexp_of=Fold_variant.foldroot.tree~leaf:{on_leaf=(fun(typepart)({name;attribute;args;create=_}:(_,part)Variant.Constructor.t):(part->Sexp.t)->letname=matchattributewith|Some(Namedname)->name|None->nameinmatchargswith|Empty->fun()->Atomname|Tuple(Troot)->funx->List(Atomname::tuple_sexpsroot.treex[])|Record(Troot)->funx->List(Atomname::record_sexpsroot.treex[]))}~node:{on_node=(funleftrightx->matchxwith|Firsta->lefta|Secondb->rightb)}infunx->sexp_of(root.convertx);;moduleFold_poly_variant=Poly_variant.Fold(Sum_acc)(* Dispatches on polymorphic variant structure to construct a sexp. [on_leaf]
callbacks usually need type annotations to guarantee they are polymorphic in the
['part] and ['name] type parameters. *)letpoly_variant(Poly_variant.Troot)=letsexp_of=Fold_poly_variant.foldroot.tree~leaf:{on_leaf=(fun(typepart)({arg;create=_}:(_,part)Poly_variant.Row.t):(part->Sexp.t)->(matchargwith|Empty{name;attribute}->letname=matchattributewith|Some(Namedname)->name|None->nameinfun()->Atomname|Value{name;attribute;value}->funx->letname=matchattributewith|Some(Namedname)->name|None->nameinList[Atomname;value.sexp_ofx]|Inheritedvalue->value.sexp_of))}~node:{on_node=(funleftrightx->matchxwith|Firsta->lefta|Secondb->rightb)}infunx->sexp_of(root.convertx);;end(* How to derive [of_sexp]: *)moduleOf_sexp=structmoduleProduct_acc=struct(* For product types, our accumulator function parses a prefix of a sexp list and
returns the result plus remaining sexps. *)type(_,'pairs)t=Sexp.tlist->'pairs*Sexp.tlistendmoduleFold_tuple=Tuple.Fold(Product_acc)moduleFold_record=Record.Fold(Product_acc)(* Most of tuple and record parsing is the same, so we share some helpers. *)leton_product_nodeleftrightsexps=letl,sexps=leftsexpsinletr,sexps=rightsexpsin(l,r),sexps;;letproduct_of_sexpof_sexpsconvertsexp=match(sexp:Sexp.t)with|Atom_->raise_s[%message"bad sexp"]|Listsexps->(matchof_sexpssexpswith|x,[]->convertx|_,_::_->raise_s[%message"bad sexp"]);;lettuple(Tuple.Troot)=letof_sexps=Fold_tuple.foldroot.tree~leaf:{on_leaf=(fun{index=_;value;access=_}sexps->matchsexpswith|[]->raise_s[%message"bad sexp"]|sexp::sexps->value.of_sexpsexp,sexps)}~node:{on_node=on_product_node}inproduct_of_sexpof_sexpsroot.convert;;letrecord(Record.Troot)=letof_sexps=Fold_record.foldroot.tree~leaf:{on_leaf=(fun{name;attribute;value;access=_}sexps->letname=matchattributewith|Some(Namedname)->name|None->nameinmatchsexpswith|List[Atomatom;sexp]::sexpswhenString.equalatomname->value.of_sexpsexp,sexps|_->raise_s[%message"bad sexp"])}~node:{on_node=on_product_node}inproduct_of_sexpof_sexpsroot.convert;;moduleSum_acc=struct(* For sum types, our accumulator function parses a sexp to a value, or [None] if
inapplicable. *)type('whole,_)t=Sexp.t->'wholeoptionendmoduleFold_variant=Variant.Fold(Sum_acc)moduleFold_poly_variant=Poly_variant.Fold(Sum_acc)(* Variant and polymorphic variant "leaf type" parsing is nontrivial, so we define
them outside the folds. *)leton_variant_leaf(typeab)({name;attribute;args;create}:(a,b)Variant.Constructor.t):Sexp.t->aoption=letname=matchattributewith|Some(Namedname)->name|None->nameinmatchargswith|Empty->(function|AtomatomwhenString.equalatomname->Some(create())|_->None)|Tuplet->letof_sexp=tupletin(function|List(Atomatom::sexps)whenString.equalatomname->Some(create(of_sexp(Listsexps)))|_->None)|Recordr->letof_sexp=recordrin(function|List(Atomatom::sexps)whenString.equalatomname->Some(create(of_sexp(Listsexps)))|_->None);;leton_poly_variant_leaf(typeab)({arg;create}:(a,b)Poly_variant.Row.t):Sexp.t->aoption=matchargwith|Empty{name;attribute}->letname=matchattributewith|Some(Namedname)->name|None->namein(function|AtomatomwhenString.equalatomname->Some(create())|_->None)|Value{name;attribute;value}->letname=matchattributewith|Some(Namedname)->name|None->namein(function|List[Atomatom;sexp]whenString.equalatomname->Some(create(value.of_sexpsexp))|_->None)|Inheritedvalue->funsexp->Option.try_with(fun()->create(value.of_sexpsexp));;(* Other details of sum type parsing are shared, so we can define some helpers. *)leton_sum_nodeleftrightsexp=Option.first_some(leftsexp)(rightsexp)letsum_of_sexpof_sexpsexp=matchof_sexpsexpwith|Somex->x|None->raise_s[%message"bad sexp"];;letvariant(Variant.Troot)=Fold_variant.foldroot.tree~leaf:{on_leaf=on_variant_leaf}~node:{on_node=on_sum_node}|>sum_of_sexp;;letpoly_variant(Poly_variant.Troot)=Fold_poly_variant.foldroot.tree~leaf:{on_leaf=on_poly_variant_leaf}~node:{on_node=on_sum_node}|>sum_of_sexp;;end(* After defining [sexp_of] and [of_sexp], the rest is relatively straightforward. *)letemptynothing_of_t={sexp_of=(funt->Nothing.unreachable_code(nothing_of_tt));of_sexp=(fun(_:Sexp.t)->raise_s[%message"bad sexp"])};;lettuplet={sexp_of=Sexp_of.tuplet;of_sexp=Of_sexp.tuplet}letrecordt={sexp_of=Sexp_of.recordt;of_sexp=Of_sexp.recordt}letvariantt={sexp_of=Sexp_of.variantt;of_sexp=Of_sexp.variantt}letpoly_variantt={sexp_of=Sexp_of.poly_variantt;of_sexp=Of_sexp.poly_variantt};;letrecursive_lazy_t={sexp_of=(funx->(Lazy.forcelazy_t).sexp_ofx);of_sexp=(funsexp->(Lazy.forcelazy_t).of_sexpsexp)};;letwith_attributet(Named.Namedname)={sexp_of=(funx->List[Atomname;t.sexp_ofx]);of_sexp=(function|List[Atomatom;sexp]whenString.equalatomname->t.of_sexpsexp|_->raise_s[%message"bad sexp"])};;end