123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631openPpxlibopenAst_builder.DefaultopenUtil.SyntaxesopenUtil.LocCtxmoduleNames=structletsynname="Ser_"^nameletlift_fixesname="lift_"^name^"_fixes"letwith_name="with_"^nameletwith_symname="with_"^name^"_sym"letppx="sym_state"letignore_attr="soteria."^ppx^".ignore"letcontext_attr="soteria."^ppx^".context"endletrecord_of_names?basenames=pexp_record(List.map(funn->(lidentn,evarn))names)baseleterr?locmsg=letloc=matchlocwithSomel->l|None->get_loc()inLocation.raise_errorf~loc"[@@deriving %s] %s"Names.ppxmsgtypecontext_attr={field:string;ctx_sym_state:Longident.t}typeignored_field={empty:expression;is_empty:expressionoption;pp:expressionoption;}typemanaged_field={sym_state:Longident.t;context:context_attroption}typefield_kind=Managedofmanaged_field|Ignoredofignored_fieldtypefield={name:string;kind:field_kind;loc:Location.t}letis_managed(f:field)=matchf.kindwithManaged_->true|Ignored_->falseletis_ignored(f:field)=matchf.kindwithManaged_->false|Ignored_->trueletmanaged_fields=List.filter_map(funf->matchf.kindwithManagedm->Some(f,m)|_->None)letignored_fields=List.filter_map(funf->matchf.kindwithIgnoredi->Some(f,i)|_->None)moduleAttributes=structopenUtil.AttributesmoduleIgnore=structletname=Names.ignore_attrletattr=declare_record~name(must"empty"**may"is_empty"**may"pp")letfind_optld=Attribute.getattrld|>Option.map@@fun(empty,(is_empty,pp))->{empty;is_empty;pp}endmoduleContext=structletname=Names.context_attrletattr=declare_record~name(must"field")letfind_optld=Attribute.getattrld|>Option.map@@function|{pexp_desc=Pexp_ident{txt=Lidentfield;_};_}->{field;ctx_sym_state=Lident"TEMP_PRE_VALIDATION"}|_->Fmt.kstr(err?loc:None)"expects [@%s { field = <field> }]"nameletvalidate_fieldfieldsf{field;ctx_sym_state=_}managed_field=let@_=with_locf.locinletctx_field=matchList.find_opt(funf->f.name=field)fieldswith|Somef->f|None->letvalid_fields=fields|>List.filter_map(funcf->iff.name=cf.namethenNoneelseSomecf.name)inFmt.kstr(err?loc:None)"%s references non-existent field %a, expected one of %a"nameFmt.(quotestring)fieldFmt.(list~sep:(Fmt.any", ")Fmt.(quotestring))valid_fieldsinifctx_field.name=f.namethenFmt.kstr(err~loc:f.loc)"%s.field cannot reference itself"name;matchctx_field.kindwith|Ignored_->Fmt.kstr(err~loc:f.loc)"%s.field cannot reference an ignored field"name|Managed{sym_state=ctx_sym_state;_}->(* update context's sym_state *)letcontext=Some{field;ctx_sym_state}in{fwithkind=Managed{managed_fieldwithcontext}}letvalidate(fields:fieldlist)=fields|>List.map@@funf->matchf.kindwith|Managed({context=Somecontext;_}asmanaged_field)->validate_fieldfieldsfcontextmanaged_field|Managed{context=None;_}|Ignored_->fendletcheck_no_extra_attrs(ld:label_declaration)=Attribute.check_unused#label_declarationldletvalidatefs=Context.validatefsendletparse_mod_t_option(ct:core_type)=let@_=with_locct.ptyp_locinmatchct.ptyp_descwith|Ptyp_constr({txt=Lident"option";_},[{ptyp_desc;_}])->(matchptyp_descwith|Ptyp_constr({txt=Ldot(path,"t");_},[])->path|_->err"expects record fields of type <Module>.t option")|_->err"expects record fields of type <Module>.t option"letmk_fieldld=Attributes.check_no_extra_attrsld;letkind=matchAttributes.Ignore.find_optldwith|Someignored->Ignoredignored|None->letsym_state=parse_mod_t_optionld.pld_typeinletcontext=Attributes.Context.find_optldinManaged{sym_state;context}in{name=ld.pld_name.txt;kind;loc=ld.pld_loc}letfields_of_td_exn(td:type_declaration)=let@_=with_loctd.ptype_lociniftd.ptype_name.txt<>"t"thenerr~loc:td.ptype_name.loc"only supports type named 't'";letlabels=matchtd.ptype_kindwith|Ptype_recordlabels->labels|_->err"only supports record types"inlabels|>List.mapmk_field|>Attributes.validate(** Folds over fields, applying f to each field and joining with join, with
empty as the base case. *)letfold_fields~empty~f~joinfields=matchfieldswith|[]->empty|hd::tl->List.fold_left(funaccfield->joinacc(ffield))(fhd)tl(** For a field Foo, creates pattern [Ser_foo(v)] *)letppat_fieldfield=letloc=get_loc()inppat_construct(lident(Names.synfield.name))(Some[%pat?v])(** For a field Foo and expression e, creates expression [Ser_foo(e)] *)letconstr_fieldfieldexpr=pexp_construct(lident(Names.synfield.name))(Someexpr)letmatch_on_synfieldsfe=letloc=get_loc()inletcases=List.map(fun(field,as_managed)->letlhs=ppat_fieldfieldinletrhs=ffieldas_managedincase~lhs~guard:None~rhs)(managed_fieldsfields)in(* we add an irrefutable case at the end, so that the pattern match is still
valid if there are no managed fields. *)letirrefutable=case~lhs:[%pat?_]~guard:None~rhs:(pexp_unreachable())inpexp_matche(cases@[irrefutable])letsyn_type_item(syn_ty:longidentoption)fields=letsyn_ctor_decl(field,{sym_state;_})=letarg_ty=ptyp_constr_dotsym_state"syn"[]inconstructor_declaration~name:(Names.synfield.name)~args:(Pcstr_tuple[arg_ty])~res:Noneinletfields=managed_fieldsfieldsinletmanifest:core_typeoption=matchsyn_tywith|Somety->Some(ptyp_constr(wlocty)[])|None->Noneinlettd=type_declaration~name:"syn"~params:[]~cstrs:[]~kind:(Ptype_variant(List.mapsyn_ctor_declfields))~private_:Public~manifestinpstr_typeRecursive[td]letpp_syn_item~locfields=letcasefield{sym_state;_}=[%exprFmt.pfft"(@[<2>%s@ %a@])"[%eestring(Names.synfield.name)][%epexp_ident_dotsym_state"pp_syn"]v]inifnot(List.existsis_managedfields)then[%striletpp_syn__=()]else[%striletpp_synft(s:syn)=[%ematch_on_synfieldscase[%exprs]]]letshow_syn_item~loc=[%striletshow_syns=Format.asprintf"%a"pp_syns]letpp_item~locfields=letf(f:field)=matchf.kindwith|Managed{sym_state;_}->[%exprFormat.fprintffmt"@[%s =@ "[%eestringf.name];(match[%epexp_field[%exprx](lidentf.name)]with|None->Format.pp_print_stringfmt"empty"|Somev->[%epexp_ident_dotsym_state"pp"]fmtv);Format.fprintffmt"@]"]|Ignored{pp=Somepp;_}->[%exprFormat.fprintffmt"@[%s =@ "[%eestringf.name];[%epp]fmt[%epexp_field[%exprx](lidentf.name)];Format.fprintffmt"@]"]|Ignored{pp=None;_}->[%exprFormat.fprintffmt"@[%s =@ <ignored>@]"[%eestringf.name]]inletbody=fold_fieldsfields~empty:[%expr()]~f~join:(funaccexpr->[%expr[%eacc];Format.fprintffmt";@ ";[%eexpr]])in[%striletppfmtx=Format.fprintffmt"@[<2>{ ";[%ebody];Format.fprintffmt"@ }@]"]letshow_item~loc=[%striletshowx=Format.asprintf"%a"ppx]letof_opt_item~locfields=letdefault_record=pexp_record(List.map(fun(f:field)->letempty=matchf.kindwith|Managed_->[%exprNone]|Ignorede->e.emptyin(lidentf.name,empty))fields)Nonein[%striletof_opt=functionNone->[%edefault_record]|Somev->v]letto_opt_item~locfields=(*
* let to_opt = function
* | { field1 = None; field2 = None; ... } -> None
* | t -> Some t
*
* IF NO IGNORED FIELDS, otherwise
* let to_opt = function
* | { field1 = None; field2 = None; ... } when <ignored_field1> = <empty1> && ... -> None
* | t -> Some t
*)letall_none_pat=ppat_record(List.map(fun(f:field)->letp=matchf.kindwith|Managed_->[%pat?None]|Ignored_->ppat_var(wlocf.name)in(lidentf.name,p))fields)Closedinmatchignored_fieldsfieldswith|[]->[%striletto_opt=function[%pall_none_pat]->None|t->Somet]|hd::tl->letis_emp(f,i)=matchi.is_emptywith|Someis_empty->[%expr[%eis_empty][%eevarf.name]]|None->[%expr[%eevarf.name]=[%ei.empty]]inletall_ignored_are_emp=List.fold_left(funaccf->[%expr[%eacc]&&[%eis_empf]])(is_emphd)tlin[%striletto_opt=function|[%pall_none_pat]when[%eall_ignored_are_emp]->None|t->Somet]letempty_item~loc=[%striletempty=None]letsm_item~locsymex_module=letsymex_module=pmod_ident(wlocsymex_module)in[%strimoduleSM=Soteria.Sym_states.State_monad.Make([%msymex_module])(structtypenonrect=toptionend)]letto_syn_item~locfields=(*
* let to_syn (st : t) : syn list =
* (List.map (fun v -> Ser_field1 v)
* (Option.fold ~none:[] ~some:Module1.to_syn st.field1))
* @ (List.map (fun v -> Ser_field2 v)
* (Option.fold ~none:[] ~some:Module2.to_syn st.field2))
*)letf(f,m)=[%exprList.map(funv->[%econstr_fieldf[%exprv]])(Option.fold~none:[]~some:[%epexp_ident_dotm.sym_state"to_syn"][%epexp_field[%exprst](lidentf.name)])]inletbody=fold_fields~empty:[%expr[]]~f~join:(funacce->[%expr[%eacc]@[%ee]])(managed_fieldsfields)inifnot(List.existsis_managedfields)then[%striletto_syn(_:t):synlist=[]]else[%striletto_syn(st:t):synlist=[%ebody]]letins_outs_item~locfields=(*
* let ins_outs_item = function
* | Ser_field1 v -> Module1.ins_outs v
* | Ser_field2 v -> Module2.ins_outs v
*)letcase_{sym_state;_}=[%expr[%epexp_ident_dotsym_state"ins_outs"]v]in[%striletins_outs(syn:syn)=[%ematch_on_synfieldscase[%exprsyn]]]letlift_syn_fix_item(target,_)=(*
* ONLY MANAGED FIELDS:
* let lift_field1_fixes = List.map (fun v -> Ser_field1 v)
*)letloc=target.locin[%strilet[%ppvar(Names.lift_fixestarget.name)]=List.map(funv->[%econstr_fieldtarget[%exprv]])]letwith_field_sym_itemfields(target:field)=(*
* DEFAULT:
* let with_field1_sym f =
* let open SM.Syntax in
* let* st_opt = SM.get_state () in
* let st = of_opt st_opt in
* let { field1; _ } = st in
* let*^ res, field1 = f field1 in
* let+ () = SM.set_state (to_opt { st with field1 }) in
* res
*
* IF CONTEXT:
* ...
* let*^ (res, field1), ctx_field =
* CtxField.SM.run_with_state ~state:st.ctx_field (f field1)
* in
* let+ () = SM.set_state (to_opt { st with field1; ctx_field }) in
* ...
*
* IF IGNORED:
* ...
* let**^ res, field1 = f field1 in
* let+ () = SM.set_state (to_opt st) in
* Soteria.Soteria_std.Compo_res.Ok res
*)let@loc=with_loctarget.locinletcontext=matchtarget.kindwith|Managed{context=Somecontext;_}->Somecontext|_->Noneinletupdated_fields=matchcontextwith|None->[target.name]|Some{field;_}->[target.name;field]inletopen_pat=List.compare_lengthsupdated_fieldsfields<>0inletst_pat=ppat_record(List.map(funl->(lidentl,pvarl))updated_fields)(ifopen_patthenOpenelseClosed)inletbind_expr=matchtarget.kindwith|Managed{context=Some{field;ctx_sym_state};_}->letctx_run=pexp_ident_dotsctx_sym_state["SM";"run_with_state"]in[%expr[%ectx_run]~state:[%eevarfield](f[%eevartarget.name])]|_->[%exprf[%eevartarget.name]]inletbind_pat=matchcontextwith|None->[%pat?res,[%ppvartarget.name]]|Some{field;_}->[%pat?(res,[%ppvartarget.name]),[%ppvarfield]]inletupdated=record_of_namesupdated_fields?base:(ifopen_patthenSome[%exprst]elseNone)inletcall_and_assign=matchtarget.kindwith|Managed_->[%exprlet*^[%pbind_pat]=[%ebind_expr]inlet+()=SM.set_state(to_opt[%eupdated])inres]|Ignored_->[%exprlet**^[%pbind_pat]=[%ebind_expr]inlet*()=SM.set_state(to_opt[%eupdated])inSM.Result.okres]in[%strilet[%ppvar(Names.with_symtarget.name)]=funf->letopenSM.Syntaxinlet*st_opt=SM.get_state()inletst=of_optst_optinlet[%pst_pat]=stin[%ecall_and_assign]]letwith_field_item(target,_)=(*
* ONLY MANAGED FIELDS:
* let with_field1 f =
* SM.Result.map_missing lift_field1_fixes (with_field1_sym f)
*)let@loc=with_loctarget.locinletwith_sym=evar(Names.with_symtarget.name)inletlift_fixes=evar(Names.lift_fixestarget.name)in[%strilet[%ppvar(Names.with_target.name)]=funf->SM.Result.map_missing[%elift_fixes]([%ewith_sym]f)]letmk_cons_prod_item~loc~kindfieldstargetmanaged_field=(*
* Helper for produce_item/consume_item. Given a field, an option wrap
* expression, generates:
*
* let+ field1 = <lift_expr> (Module1.<produce/consume> v st.field1) in
* to_opt { st with field1 }
*
* OR, if context field:
* let+ (field1, ctx_field) =
* <lift_expr>
* @@ CtxField.<Producer/Consumer>.run_with_state ~state:st.ctx_field
* @@ Module1.<produce/consume> v st.field1
* in
* to_opt { st with field1; ctx_field }
*
* where <lift_expr> is either identity (for produce) or a fixes-lifting
* function (for consume)
*)letfn_name,module_name=matchkindwith|`Produce->("produce","Producer")|`Consume->("consume","Consumer")inletfn_expr=pexp_ident_dotmanaged_field.sym_statefn_nameinletfield=pexp_field[%exprst](lidenttarget.name)inletexpr=[%expr[%efn_expr]v[%efield]]inletexpr=matchtarget.kindwith|Managed{context=Some{field;ctx_sym_state};_}->letctx_run_with=pexp_ident_dotsctx_sym_state["SM";module_name;"run_with_state"]inletctx_field=pexp_field[%exprst](lidentfield)in[%expr[%ectx_run_with]~state:[%ectx_field][%eexpr]]|_->exprinletexpr=matchkindwith|`Produce->expr|`Consume->letlift_fixes=evar(Names.lift_fixestarget.name)in[%exprlet+?fixes=[%eexpr]in[%elift_fixes]fixes]inletupdated_fields=matchmanaged_field.contextwith|None->[target.name]|Some{field;_}->[target.name;field]inletassign_pat=ppat_tuple(List.mappvarupdated_fields)inletis_open=List.compare_lengthsupdated_fieldsfields<>0inletupdated=record_of_namesupdated_fields?base:(ifis_openthenSome[%exprst]elseNone)in[%exprlet+[%passign_pat]=[%eexpr]into_opt[%eupdated]]letmk_cons_prod_match~loc~kindfields=match_on_synfields(mk_cons_prod_item~loc~kindfields)[%exprsyn]letproduce_item~locfields=(*
* let produce (syn : syn) (st : t option) =
* let open SM.Symex.Producer.Syntax in
* let st = of_opt st in
* match syn with
* | Ser_field1 v ->
* let+ field1 = Module1.produce v st.field1 in
* to_opt { st with field1 }
* | Ser_field2 v -> ...
*
* IF CONTEXT FIELD:
* | Ser_field1 v ->
* let+ (field1, ctx_field) =
* CtxField.Producer.run_with_state ~state:st.ctx_field
* (Module1.produce v st.field1)
* in
* to_opt { st with field1; ctx_field }
*)ifnot(List.existsis_managedfields)then[%striletproduce(syn:syn)()=matchsynwith_->.]else[%striletproduce(syn:syn)(st:toption):toptionSM.Symex.Producer.t=letopenSM.Symex.Producer.Syntaxinletst=of_optstin[%emk_cons_prod_match~loc~kind:`Producefields]]letconsume_item~locfields=(*
* let consume (syn : syn) (st : t option) =
* let open SM.Symex.Consumer.Syntax in
* let st = of_opt st in
* match syn with
* | Ser_field1 v ->
* let+ field1 =
* let+? fixes = Module1.consume v st.field1 in
* lift_field1_fixes fixes
* in
* to_opt { st with field1 }
* | Ser_field2 v -> ...
*
* IF CONTEXT FIELD:
* | Ser_field1 v ->
* let+ (field1, ctx_field) =
* let+? fixes =
* CtxField.Consumer.run_with_state ~state:st.ctx_field
* (Module1.consume v st.field1)
* in
* lift_field1_fixes fixes
* in
* to_opt { st with field1; ctx_field }
*)ifnot(List.existsis_managedfields)then[%striletconsume(syn:syn)()=matchsynwith_->.]else[%striletconsume(syn:syn)(st:toption):(toption,synlist)SM.Symex.Consumer.t=letopenSM.Symex.Consumer.Syntaxinletst=of_optstin[%emk_cons_prod_match~loc~kind:`Consumefields]]letmake_impl~loc~symex_module~syn_ty(td:type_declaration)=let@loc=with_loclocinletfields=fields_of_td_exntdin[sm_item~locsymex_module;pp_item~locfields;show_item~loc;syn_type_itemsyn_tyfields;pp_syn_item~locfields;show_syn_item~loc;of_opt_item~locfields;to_opt_item~locfields;empty_item~loc;to_syn_item~locfields;ins_outs_item~locfields;]@List.maplift_syn_fix_item(managed_fieldsfields)@List.map(with_field_sym_itemfields)fields@List.mapwith_field_item(managed_fieldsfields)@[produce_item~locfields;consume_item~locfields]letstr_type_decl~loc~path:_(_rec,tds)symex_modulesyn_ty=let@_=with_loclocinletsymex_module=matchsymex_modulewith|Some{pexp_desc=Pexp_construct({txt;_},None);_}->txt|_->err"expected { symex = <Module> }"inletsyn_ty=matchsyn_tywith|Some{pexp_desc=Pexp_ident{txt;_};_}->Sometxt|None->None|_->err"expected { syn_ty = <ty> }"inmatchtdswith|[td]->make_impl~loc~symex_module~syn_tytd|_->err"expects exactly one type declaration"letregister()=letsymex_arg=Deriving.Args.arg"symex"Ast_pattern.__inletsyn_ty_arg=Deriving.Args.arg"syn"Ast_pattern.__inletstr_args=Deriving.Args.(empty+>symex_arg+>syn_ty_arg)inletstr=Deriving.Generator.makestr_argsstr_type_declinDeriving.addNames.ppx~str_type_decl:str|>Deriving.ignore