123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429(*********************************************************************************)(* Ocf *)(* *)(* Copyright (C) 2015-2024 INRIA. All rights reserved. *)(* *)(* This program is free software; you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License as *)(* published by the Free Software Foundation, version 3 of the License. *)(* *)(* This program is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Library General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public *)(* License along with this program; if not, write to the Free Software *)(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *)(* 02111-1307 USA *)(* *)(* As a special exception, you have permission to link this program *)(* with the OCaml compiler and distribute executables, as long as you *)(* follow the requirements of the GNU GPL in regard to all of the *)(* software in the executable aside from the OCaml compiler. *)(* *)(* Contact: Maxence.Guesdon@inria.fr *)(* *)(*********************************************************************************)(** Ppx processor for Ocf. *)letmkloc=Location.mklocletlid?(loc=Location.none)s=letb=Lexing.from_stringsinletp=loc.Location.loc_startinletb={bwithLexing.lex_start_p=p;lex_curr_p=p}inmkloc(Parse.longidentb)locleterrorlocmsg=raise(Location.Error(Location.error~locmsg))letkerrorloc=Printf.ksprintf(errorloc)openPpxlibopenAst_helpermoduleLocation=Ppxlib_ast__Import.Location(*
open Ast_mapper
open Ast_helper
open Asttypes
open Parsetree
open Longident
*)moduleSMap=Map.Make(String)letapply?loceargs=Ast_helper.Exp.apply?loce(List.map(fune->(Nolabel,e))args)letmk_stringlocs=Exp.constant~loc(Pconst_string(s,Location.none,None))letconsloc=lid~loc"(::)"letempty_list=Ast_helper.Exp.construct(lid"[]")Noneletmk_listlocl=List.fold_right(funeacc->Ast_helper.Exp.construct(consloc)(Some(Ast_helper.Exp.tuple[e;acc])))lempty_list(*c==v=[List.list_remove_doubles]=1.0====*)letlist_remove_doubles?(pred=(=))l=List.fold_left(funacce->ifList.exists(prede)accthenaccelsee::acc)[](List.revl)(*/c==v=[List.list_remove_doubles]=1.0====*)letocf_att_prefix="ocf"letlen_ocf_att_prefix=String.lengthocf_att_prefixletget_ocf_attr=function|swhens=ocf_att_prefix->Some""|s->letlen=String.lengthsiniflen<len_ocf_att_prefix+1thenNoneelseifString.subs0len_ocf_att_prefix=ocf_att_prefix&&String.getslen_ocf_att_prefix='.'thenSome(String.subs(len_ocf_att_prefix+1)(len-(len_ocf_att_prefix+1)))elseNonelethas_ocf_attributeattrs=List.exists(fun{attr_name}->get_ocf_attrattr_name.txt<>None)attrstypefield={name:stringLocation.loc;label:stringLocation.loc;doc:expressionoption;params:stringlist;wrapper:expressionoption;default:expressionoption;}letparams_of_type_paramsl=letfctacc=matchct.ptyp_descwithPtyp_vars->s::acc|_->accinlist_remove_doubles(List.fold_rightfl[])letattribute_nameatts=trySome(List.find(fun{attr_name={txt}}->txt=name)atts)withNot_found->Noneletattribute=letrecitername=function[]->None|h::q->matchattribute_namehwithNone->iternameq|Somex->Somexiniterletmk_fieldl=letlabel=matchattribute(ocf_att_prefix^".label")[l.pld_attributes;l.pld_type.ptyp_attributes]with|None->l.pld_name|Some{attr_payload=PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_string(label,_,_));pexp_loc},_)}]}->{txt=label;loc=pexp_loc}|Some{attr_name=x}->kerrorx.loc"Invalid expression for %s.label; a string is expected"ocf_att_prefixinletdoc=matchattribute(ocf_att_prefix^".doc")[l.pld_attributes;l.pld_type.ptyp_attributes]with|None->None|Some{attr_payload=PStr[{pstr_desc=Pstr_eval(e,_)}]}->Somee|Some{attr_name=x}->kerrorx.loc"Invalid expression for %s.doc; an expression is expected"ocf_att_prefixinlet(wrapper,default)=letatts=[l.pld_attributes;l.pld_type.ptyp_attributes]inmatchattributeocf_att_prefixattswith|None->beginletwrapper=matchattribute(ocf_att_prefix^".wrapper")attswithNone->None|Some{attr_payload=PStr[{pstr_desc=Pstr_eval(e,_)}]}->Somee|Some{attr_name=x}->kerrorx.loc"Invalid payload for %s.wrapper; an expression is expected"ocf_att_prefixinletdefault=matchattribute(ocf_att_prefix^".default")attswithNone->None|Some{attr_payload=PStr[{pstr_desc=Pstr_eval(e,_)}]}->Somee|Some{attr_name=x}->kerrorx.loc"Invalid payload for %s.wrapper; an expression is expected"ocf_att_prefixin(wrapper,default)end|Some{attr_payload=PStr[{pstr_desc=Pstr_eval(e,_)}]}->beginmatche.pexp_descwith|Pexp_tuple[wrapper;default]->(Somewrapper,Somedefault)|_->kerrore.pexp_loc"Invalid expression; a pair of expressions is expected"end|Some{attr_name=x}->kerrorx.loc"Invalid expression for %s; a pair of expressions is expected"ocf_att_prefixinletparams=matchl.pld_type.ptyp_descwithPtyp_constr(_,params)->params_of_type_paramsparams|Ptyp_vars->[s]|_->[]in{name=l.pld_name;label;doc;params;wrapper;default}letmk_defaultdeclfields=letffd=letexpr=matchfd.defaultwithSomedef->def|None->Exp.ident(lid~loc:fd.name.locfd.name.txt)in(lid~loc:fd.name.locfd.name.txt,expr)inletrecord=Exp.record(List.mapffields)Noneinletpat=Pat.var(mkloc("default_"^decl.ptype_name.txt)decl.ptype_loc)inletexpr=List.fold_right(funfdexpr->matchfd.defaultwith|Some_->expr|None->letpat=Pat.varfd.nameinExp.fun_(Labelledfd.name.txt)Nonepatexpr)fieldsrecordinVb.mkpatexprletmk_wrapperdeclfields=letloc=decl.ptype_locinletparams=params_of_type_params(List.mapfstdecl.ptype_params)inletw_namefd=Printf.sprintf"__wrapper_%s"fd.name.txtinletw_lidfd=Exp.ident(lid~loc:fd.name.loc(w_namefd))inletmk_wrapperexprfd=letloc=fd.name.locinletpat=Pat.var{loc;txt=w_namefd}inletapp=matchfd.params,fd.wrapperwith|[],None|_::_::_,None->kerrorfd.name.loc"Missing Ocf wrapper for field %s"fd.name.txt|[],Somee->e|[param],None->Exp.ident(lid~loc:fd.name.loc("wrapper_"^param))|params,Somee->apply~loc:fd.name.loce(List.map(funp->Exp.ident(lid~loc:fd.name.loc("wrapper_"^p)))fd.params)in[%exprlet[%ppat]=[%eapp]in[%eexpr]]inletto_json_exprs=letffd=letloc=fd.name.locinletto_json=[%expr[%ew_lidfd].Ocf.Wrapper.to_json?with_doc]inletfd_exp=Exp.field[%exprt](lid~loc:fd.name.loc(fd.name.txt))inletassoc=[%expr([%emk_stringfd.label.locfd.label.txt],[%eto_json][%efd_exp])]inlete=matchfd.docwithNone->[%expr[[%eassoc]]]|Someexp_doc->[%exprmatchwith_docwith|Sometrue->[[%emk_stringfd.label.locfd.label.txt],`String[%eexp_doc];[%eassoc];]|_->[[%eassoc]]]ineinmk_listdecl.ptype_loc(List.mapffields)inletexpr_from_assocs=letffd=letloc=fd.name.locinletexpr_v=letexp_field=Exp.field[%exprdef](lid~loc(fd.name.txt))in[%exprletdefault=matchdefwith|Somedef->[%eexp_field]|None->[%ematchfd.defaultwith|Somedef->def|None->Exp.ident(lid~loc:fd.name.locfd.name.txt)]inget[%ew_lidfd]default[%emk_stringfd.label.locfd.label.txt]map]in(lid~loc:fd.name.locfd.name.txt,expr_v)inExp.record(List.mapffields)Noneinletexpr=[%exprletto_j?with_doct=`Assoc(List.flatten[%eto_json_exprs])inletgetwdeflabelmap=matchOcf.SMap.findlabelmapwith|exceptionNot_found->def|json->tryw.Ocf.Wrapper.from_jsonjsonwith|Ocf.Errore->Ocf.error_at_path[label]e|e->Ocf.exn_at_path[label]einletfrom_assocsmapdef=[%eexpr_from_assocs]inletfrom_j?def=function|`Assocl->beginletassocs=List.fold_left(funacc(k,v)->Ocf.SMap.addkvacc)Ocf.SMap.emptylinfrom_assocsassocsdefend|(json:Yojson.Safe.t)->Ocf.invalid_valuejsoninOcf.Wrapper.maketo_jfrom_j]inletexpr=List.fold_leftmk_wrapperexprfieldsinletexpr=letfparamexpr=letpat=Pat.var{loc=decl.ptype_loc;txt="wrapper_"^param}in[%exprfun[%ppat]->[%eexpr]]inList.fold_rightfparamsexprinletexpr=letffdexpr=matchfd.defaultwith|Some_->expr|None->letpat=Pat.varfd.nameinExp.fun_(Labelledfd.name.txt)NonepatexprinList.fold_rightffieldsexprinletpat=Pat.var(mkloc(decl.ptype_name.txt^"_wrapper")decl.ptype_loc)inVb.mkpatexprletgeneratedecl=matchdecl.ptype_kindwith|Ptype_recordfields->beginletfields=List.mapmk_fieldfieldsinletdefault=mk_defaultdeclfieldsinletwrapper=mk_wrapperdeclfieldsin[default;wrapper]end|_->kerrordecl.ptype_loc"Only record types can have @@%s attribute"ocf_att_prefixletfold_structureaccitem=matchitem.pstr_descwith|(Pstr_type(_,l))->lettype_decls=List.filter(fundecl->has_ocf_attributedecl.ptype_attributes)linletbindings=List.flatten(List.mapgeneratetype_decls)inbeginmatchbindingswith|[]->item::acc|_->letloc=item.pstr_locinletattron=letstri=ifonthen[%stri"-39"]else[%stri"+39"]in{attr_name=mkloc"warning"item.pstr_loc;attr_loc=item.pstr_loc;attr_payload=PStr[stri];}inletwarnon={pstr_loc=item.pstr_loc;pstr_desc=Pstr_attribute(attron);}inletdecl={pstr_desc=Pstr_value(Recursive,bindings);pstr_loc=item.pstr_loc;}in(warnfalse)::decl::(warntrue)::item::accend|_->item::accletstructure_mappersuper_structuremapperstructure=letstructure=List.fold_leftfold_structure[]structureinsuper_structure(List.revstructure)classmapper=object(self)inheritAst_traverse.mapassupermethod!structures=structure_mappersuper#structureselfsendletmapper=newmapperlet()=Driver.register_transformation~impl:mapper#structure"ocf.ppx"