123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844(************************************************************************)(* * The Rocq Prover / The Rocq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)openMinimlopenConstropenDeclarationsopenMod_declarationsopenNamesopenModPathopenLibnamesopenPpopenCErrorsopenUtilopenTableopenExtractionopenModutilopenCommon(****************************************)(*S Part I: computing Rocq environment. *)(****************************************)lettoplevel_env()=letmp,struc=Safe_typing.flatten_env(Global.safe_env())inmp,List.revstrucletenvironment_untildir_opt=letrecparse=function|[]whenOption.is_emptydir_opt->[toplevel_env()]|[]->[]|d::l->letmeb=Modops.destr_nofunctor(MPfiled)(mod_type@@Global.lookup_module(MPfiled))inmatchdir_optwith|Somed'whenDirPath.equaldd'->[MPfiled,meb]|_->(MPfiled,meb)::(parsel)inparse(Library.loaded_libraries())(*s Visit:
a structure recording the needed dependencies for the current extraction *)moduletypeVISIT=sigtypet(* Environment of visited data *)valmake:unit->t(* Add the module_path and all its prefixes to the mp visit list.
We'll keep all fields of these modules. *)valadd_mp_all:t->ModPath.t->unit(* Add reference / ... in the visit lists.
These functions silently add the mp of their arg in the mp list *)valadd_ref:t->global->unitvaladd_kn:t->KerName.t->InfvInst.t->unitvaladd_decl_deps:t->ml_decl->unitvaladd_spec_deps:t->ml_spec->unit(* Test functions:
is a particular object a needed dependency for the current extraction ? *)valneeded_ind:t->MutInd.t->InfvInst.t->boolvalneeded_cst:t->Constant.t->InfvInst.t->boolvalneeded_mp:t->ModPath.t->boolvalneeded_mp_all:t->ModPath.t->boolendmoduleVisit:VISIT=structmoduleKNOrd=structtypet=KerName.t*InfvInst.tletcompare(kn1,i1)(kn2,i2)=letc=KerName.comparekn1kn2inifInt.equalc0thenInfvInst.comparei1i2elsecendmoduleKNset=Set.Make(KNOrd)typet={mutablekn:KNset.t;mutablemp:ModPath.Set.t;mutablemp_all:ModPath.Set.t}(* the imperative internal visit lists *)letmake()={kn=KNset.empty;mp=ModPath.Set.empty;mp_all=ModPath.Set.empty;}(* the accessor functions *)letneeded_indviinst=KNset.mem(MutInd.useri,inst)v.knletneeded_cstvcinst=KNset.mem(Constant.userc,inst)v.knletneeded_mpvmp=ModPath.Set.memmpv.mp||ModPath.Set.memmpv.mp_allletneeded_mp_allvmp=ModPath.Set.memmpv.mp_allletadd_mpvmp=check_loaded_modfilemp;v.mp<-ModPath.Set.union(prefixes_mpmp)v.mpletadd_mp_allvmp=check_loaded_modfilemp;v.mp<-ModPath.Set.union(prefixes_mpmp)v.mp;v.mp_all<-ModPath.Set.addmpv.mp_allletadd_knvkninst=v.kn<-KNset.add(kn,inst)v.kn;add_mpv(KerName.modpathkn)letadd_refvr=letopenGlobRefinmatchr.globwith|ConstRefc->add_knv(Constant.userc)r.inst|IndRef(ind,_)|ConstructRef((ind,_),_)->add_knv(MutInd.userind)r.inst|VarRef_->assertfalseletadd_decl_depsvdecl=decl_iter_references(funkn->add_refvkn)(funr->add_refvr)(funr->add_refvr)declletadd_spec_depsvspec=spec_iter_references(funr->add_refvr)(funr->add_refvr)(funr->add_refvr)specendletget_mono_inst_univs=function|Monomorphic->[InfvInst.empty]|Polymorphicuctx->InfvInst.generateuctxletget_mono_inst=function|SFBconstcb->get_mono_inst_univscb.const_universes|SFBmindmib->get_mono_inst_univsmib.mind_universes|SFBrules_->[InfvInst.empty]|SFBmodule_|SFBmodtype_->assertfalseletadd_field_labelvenvmp=function|(lab,(SFBconst_|SFBmind_|SFBrules_asf))->letinsts=get_mono_instfinList.iter(funinst->Visit.add_knvenv(KerName.makemplab)inst)insts|(lab,(SFBmodule_|SFBmodtype_))->Visit.add_mp_allvenv(MPdot(mp,lab))letrecadd_labelsvenvmp=function|MoreFunctor(_,_,m)->add_labelsvenvmpm|NoFunctorsign->List.iter(funf->add_field_labelvenvmpf)signexceptionImpossibleletcheck_arityenvcb=lett=cb.const_typeinifReduction.is_arityenvtthenraiseImpossibleletget_bodylbody=EConstr.of_constrlbodyletcheck_fixenvsgcbi=matchcb.const_bodywith|Deflbody->(matchEConstr.kindsg(get_bodylbody)with|Fix((_,j),recd)whenInt.equalij->check_arityenvcb;(true,recd)|CoFix(j,recd)whenInt.equalij->check_arityenvcb;(false,recd)|_->raiseImpossible)|Undef_|OpaqueDef_|Primitive_|Symbol_->raiseImpossibleletprec_declaration_equalsg(na1,ca1,ta1)(na2,ca2,ta2)=Array.equal(Context.eq_annotName.equal(EConstr.ERelevance.equalsg))na1na2&&Array.equal(EConstr.eq_constrsg)ca1ca2&&Array.equal(EConstr.eq_constrsg)ta1ta2letfactor_fixenvsglcbmsb=let_,recdascheck=check_fixenvsgcb0inletn=Array.length(letfi,_,_=recdinfi)inifInt.equaln1then[|l|],recd,msbelsebeginifList.lengthmsb<n-1thenraiseImpossible;letmsb',msb''=List.chop(n-1)msbinletlabels=Array.makenlinList.iteri(funj->function|(l,SFBconstcb')->letcheck'=check_fixenvsgcb'(j+1)inifnot((fstcheck:bool)==(fstcheck')&&prec_declaration_equalsg(sndcheck)(sndcheck'))thenraiseImpossible;labels.(j+1)<-l;|_->raiseImpossible)msb';labels,recd,msb''end(** Expanding a [module_alg_expr] into a version without abbreviations
or functor applications. This is done via a detour to entries
(hack proposed by Elie)
*)letvm_state=(* VM bytecode is not needed here *)letvm_handler___()=(),Vmemitcodes.BCuncompiledin((),{Mod_typing.vm_handler})letexpand_mexprenvmpme=letinl=Declaremods.default_inline_level()in(* hack: in order not to overwrite the module binding mp, we first give it a
name that should not be part of the env and then substitute it away *)letmp0=ModPath.dummyinletstate=((Environ.universesenv,Univ.UnivConstraints.empty),Reductionops.inferred_universes)inletmb,(_,cst),_=Mod_typing.translate_modulestatevm_stateenvmp0inl(MExpr([],me,None))inletsign=mod_typembinletreso=mod_deltambinModops.subst_modtype_signature_and_resolvermp0mpsignresoletexpand_modtypeenvmpme=letinl=Declaremods.default_inline_level()inletstate=((Environ.universesenv,Univ.UnivConstraints.empty),Reductionops.inferred_universes)inletmtb,_cst,_=Mod_typing.translate_modtypestatevm_stateenvmpinl([],me)inmtbletno_delta=Mod_subst.empty_delta_resolverletflatten_modtypeenvmpme_algstruc_opt=matchstruc_optwith|Someme->me,no_deltamp|None->letmtb=expand_modtypeenvmpme_alginmod_typemtb,mod_deltamtb(** Ad-hoc update of environment, inspired by [Mod_typing.check_with_aux_def].
*)letenv_for_mtb_with_defenvmpmeresoidl=letstruc=Modops.destr_nofunctormpmeinletl=List.hdidlinletspot=function(l',SFBconst_)->Id.equalll'|_->falseinletbefore=fst(List.split_whenspotstruc)inEnviron.Internal.overwrite_structurempbeforeresoenvletmake_cstresolvermpl=Mod_subst.constant_of_delta_knresolver(KerName.makempl)letmake_mindresolvermpl=Mod_subst.mind_of_delta_knresolver(KerName.makempl)(* The overwrite functions below are basically a hack to work around the fact
that extraction breaks all kinds of reasonable invariants regarding modules.
Due to the way signatures are handled, modules can be overwritten in the
environment to replace a concrete body by its interface. Needless to say
that this is probably wildly unsound, but it works good enough (TM). *)(* From a [structure_body] (i.e. a list of [structure_field_body])
to specifications. *)letrecextract_structure_spectablevenvenvmpreso=function|[]->[]|(l,SFBconstcb)::msig->letinsts=get_mono_inst_univscb.const_universesinletc=make_cstresomplinletmapinst=extract_constant_spectableenvcinstcbinletconsts=List.mapmapinstsinletspecs=extract_structure_spectablevenvenvmpresomsiginletfoldsspecs=iflogical_specsthenspecselselet()=Visit.add_spec_depsvenvsin(l,Specs)::specsinList.fold_rightfoldconstsspecs|(l,SFBmindmib)::msig->letinsts=get_mono_inst_univsmib.mind_universesinletmind=make_mindresomplinletmapinst=Sind(extract_inductivetableenvmindinst)inletminds=List.mapmapinstsinletspecs=extract_structure_spectablevenvenvmpresomsiginletfoldsspecs=iflogical_specsthenspecselselet()=Visit.add_spec_depsvenvsin(l,Specs)::specsinList.fold_rightfoldmindsspecs|(l,SFBrules_)::msig->letspecs=extract_structure_spectablevenvenvmpresomsiginspecs|(l,SFBmodulemb)::msig->letspecs=extract_structure_spectablevenvenvmpresomsiginletmp=MPdot(mp,l)inletspec=extract_mbody_spectablevenvenvmpmbin(l,Smodulespec)::specs|(l,SFBmodtypemtb)::msig->letspecs=extract_structure_spectablevenvenvmpresomsiginletmp=MPdot(mp,l)inletspec=extract_mbody_spectablevenvenvmpmtbin(l,Smodtypespec)::specs(* From [module_expression] to specifications *)(* Invariant: the [me_alg] given to [extract_mexpr_spec] and
[extract_mexpression_spec] should come from a [mod_type_alg] field.
This way, any encountered [MEident] should be a true module type. *)andextract_mexpr_spectablevenvenvmp1(me_struct_o,me_alg)=matchme_algwith|MEidentmp->let()=Visit.add_mp_allvenvmpinMTidentmp|MEwith(me',WithDef(idl,(c,ctx)))->let()=matchctxwith|None->()|Someauctx->(* XXX *)ifArray.is_empty(UVars.AbstractContext.namesauctx).qualsthen()elseuser_errPp.(str"Extraction of \"with Definition\" clauses not supported for sort polymorphic definitions.")inletme_struct,delta=flatten_modtypeenvmp1me'me_struct_oinletenv'=env_for_mtb_with_defenvmp1me_structdeltaidlinletmt=extract_mexpr_spectablevenvenvmp1(None,me')inletsg=Evd.from_envenvin(matchextract_with_typetableenv'sg(EConstr.of_constrc)with(* cb may contain some kn *)|None->mt|Some(vl,typ)->let()=type_iter_references(funr->Visit.add_refvenvr)typinMTwith(mt,ML_With_type(InfvInst.empty,idl,vl,typ)))|MEwith(me',WithMod(idl,mp))->let()=Visit.add_mp_allvenvmpinMTwith(extract_mexpr_spectablevenvenvmp1(None,me'),ML_With_module(idl,mp))|MEapply_->(* No higher-order module type in OCaml : we use the expanded version *)letme_struct,delta=flatten_modtypeenvmp1me_algme_struct_oinextract_msignature_spectablevenvenvmp1deltame_structandextract_mexpression_spectablevenvenvmp1(me_struct,me_alg)=matchme_algwith|MEMoreFunctorme_alg'->letmbid,mtb,me_struct'=matchme_structwith|MoreFunctor(mbid,mtb,me')->(mbid,mtb,me')|_->assertfalseinletmp=MPboundmbidinletenv'=Environ.Internal.overwrite_module_parametermbidmtbenvinMTfunsig(mbid,extract_mbody_spectablevenvenvmpmtb,extract_mexpression_spectablevenvenv'mp1(me_struct',me_alg'))|MENoFunctorm->extract_mexpr_spectablevenvenvmp1(Someme_struct,m)andextract_msignature_spectablevenvenvmp1reso=function|NoFunctorstruc->letenv'=Environ.Internal.overwrite_structuremp1strucresoenvinMTsig(mp1,extract_structure_spectablevenvenv'mp1resostruc)|MoreFunctor(mbid,mtb,me)->letmp=MPboundmbidinletenv'=Environ.Internal.overwrite_module_parametermbidmtbenvinMTfunsig(mbid,extract_mbody_spectablevenvenvmpmtb,extract_msignature_spectablevenvenv'mp1resome)andextract_mbody_spec:'a.State.t->_->_->_->'ageneric_module_body->_=funtablevenvenvmpmb->matchmod_type_algmbwith|Somety->extract_mexpression_spectablevenvenvmp(mod_typemb,ty)|None->extract_msignature_spectablevenvenvmp(mod_deltamb)(mod_typemb)(* From a [structure_body] (i.e. a list of [structure_field_body])
to implementations.
NB: when [all=false], the evaluation order of the list is
important: last to first ensures correct dependencies.
*)letrecextract_structuretableaccessvenvenvmpreso~all=function|[]->[]|(l,SFBconstcb)::struc->letsg=Evd.from_envenvinletfix,struc=matchfactor_fixenvsglcbstrucwith|(vl,recd,struc)->Some(vl,recd),struc|exceptionImpossible->None,strucinletms=extract_structuretableaccessvenvenvmpreso~allstrucinletinsts=get_mono_inst_univscb.const_universesinletc=make_cstresomplinletmapinst=matchfixwith|None->letb=Visit.needed_cstvenvcinstinifall||bthenletd=extract_constanttableaccessenvcinstcbinif(notb)&&(logical_decld)thenNoneelselet()=Visit.add_decl_depsvenvdinSome(l,SEdecld)elseNone|Some(vl,recd)->letvc=Array.map(make_cstresomp)vlinletb=Array.exists(funvf->Visit.needed_cstvenvvfinst)vcinifall||bthenletd=extract_fixpointtableenvsgvcinstrecdinif(notb)&&(logical_decld)thenNoneelselet()=Visit.add_decl_depsvenvdinSome(l,SEdecld)elseNoneinletconsts=List.map_filtermapinstsinconsts@ms|(l,SFBmindmib)::struc->letms=extract_structuretableaccessvenvenvmpreso~allstrucinletinsts=get_mono_inst_univsmib.mind_universesinletmind=make_mindresomplinletmapinst=letb=Visit.needed_indvenvmindinstinifall||bthenletd=Dind(extract_inductivetableenvmindinst)inif(notb)&&(logical_decld)thenNoneelselet()=Visit.add_decl_depsvenvdinSome(l,SEdecld)elseNoneinletinds=List.map_filtermapinstsininds@ms|(l,SFBrulesrrb)::struc->letinst=InfvInst.emptyin(* FIXME ? *)letb=List.exists(fun(cst,_)->Visit.needed_cstvenvcstinst)rrb.rewrules_rulesinletms=extract_structuretableaccessvenvenvmpreso~allstrucinifall||bthenbeginList.iter(fun(cst,_)->Table.add_symbol_rule(State.get_tabletable)(ConstRefcst)l)rrb.rewrules_rules;msendelsems|(l,SFBmodulemb)::struc->letms=extract_structuretableaccessvenvenvmpreso~allstrucinletmp=MPdot(mp,l)inletall'=all||Visit.needed_mp_allvenvmpinifall'||Visit.needed_mpvenvmpthen(l,SEmodule(extract_moduletableaccessvenvenvmp~all:all'mb))::mselsems|(l,SFBmodtypemtb)::struc->letms=extract_structuretableaccessvenvenvmpreso~allstrucinletmp=MPdot(mp,l)inifall||Visit.needed_mpvenvmpthen(l,SEmodtype(extract_mbody_spectablevenvenvmpmtb))::mselsems(* From [module_expr] and [module_expression] to implementations *)andextract_mexprtableaccessvenvenvmp=function|MEwith_->assertfalse(* no 'with' syntax for modules *)|mewhenlang()!=Ocaml->(* In Haskell/Scheme, we expand everything.
For now, we also extract everything, dead code will be removed later
(see [Modutil.optimize_struct]. *)letsign,delta=expand_mexprenvmpmeinextract_msignaturetableaccessvenvenvmpdelta~all:truesign|MEidentmp->ifis_modfilemp&¬(State.get_modulartable)thenerror_MPfile_as_modmpfalse;Visit.add_mp_allvenvmp;Miniml.MEidentmp|MEapply(me,arg)->Miniml.MEapply(extract_mexprtableaccessvenvenvmpme,extract_mexprtableaccessvenvenvmp(MEidentarg))andextract_mexpressiontableaccessvenvenvmpmty=function|MENoFunctorme->extract_mexprtableaccessvenvenvmpme|MEMoreFunctorme->let(mbid,mtb,mty)=matchmtywith|MoreFunctor(mbid,mtb,mty)->(mbid,mtb,mty)|NoFunctor_->assertfalseinletmp1=MPboundmbidinletenv'=Environ.Internal.overwrite_module_parametermbidmtbenvinMiniml.MEfunctor(mbid,extract_mbody_spectablevenvenvmp1mtb,extract_mexpressiontableaccessvenvenv'mpmtyme)andextract_msignaturetableaccessvenvenvmpreso~all=function|NoFunctorstruc->letenv'=Environ.Internal.overwrite_structurempstrucresoenvinMiniml.MEstruct(mp,extract_structuretableaccessvenvenv'mpreso~allstruc)|MoreFunctor(mbid,mtb,me)->letmp1=MPboundmbidinletenv'=Environ.Internal.overwrite_module_parametermbidmtbenvinMiniml.MEfunctor(mbid,extract_mbody_spectablevenvenvmp1mtb,extract_msignaturetableaccessvenvenv'mpreso~allme)andextract_moduletableaccessvenvenvmp~allmb=(* A module has an empty [mod_expr] when :
- it is a module variable (for instance X inside a Module F [X:SIG])
- it is a module assumption (Declare Module).
Since we look at modules from outside, we shouldn't have variables.
But a Declare Module at toplevel seems legal (cf #2525). For the
moment we don't support this situation. *)letimpl=matchMod_declarations.mod_exprmbwith|Abstract->error_no_module_exprmp|Algebraicme->extract_mexpressiontableaccessvenvenvmp(mod_typemb)me|Struct(reso,sign)->(* This module has a signature, otherwise it would be FullStruct.
We extract just the elements required by this signature. *)let()=add_labelsvenvmp(mod_typemb)inletsign=Modops.annotate_struct_bodysign(mod_typemb)inextract_msignaturetableaccessvenvenvmpreso~all:falsesign|FullStruct->extract_msignaturetableaccessvenvenvmp(mod_deltamb)~all(mod_typemb)in(* Slight optimization: for modules without explicit signatures
([FullStruct] case), we build the type out of the extracted
implementation *)lettyp=matchMod_declarations.mod_exprmbwith|FullStruct->assert(Option.is_empty@@mod_type_algmb);mtyp_of_mexprimpl|_->extract_mbody_spectablevenvenvmpmbin{ml_mod_expr=impl;ml_mod_type=typ}letmono_environmenttable~opaque_accessrefsmpl=letvenv=Visit.make()inlet()=List.iter(funr->Visit.add_refvenvr)refsinlet()=List.iter(funmp->Visit.add_mp_allvenvmp)mplinletenv=Global.env()inletl=List.rev(environment_untilNone)inList.rev_map(fun(mp,struc)->mp,extract_structuretableopaque_accessvenvenvmp(no_deltamp)~all:(Visit.needed_mp_allvenvmp)struc)l(**************************************)(*S Part II : Input/Output primitives *)(**************************************)letdescr()=matchlang()with|Ocaml->Ocaml.ocaml_descr|Haskell->Haskell.haskell_descr|Scheme->Scheme.scheme_descr|JSON->Json.json_descr(* From a filename string "foo.ml" or "foo", builds "foo.ml" and "foo.mli"
Works similarly for the other languages. *)letdefault_id=Id.of_string"Main"letmono_filenamef=letd=descr()inmatchfwith|None->None,None,default_id|Somef->letf=ifFilename.check_suffixfd.file_suffixthenFilename.chop_suffixfd.file_suffixelsefinletid=iflang()!=Haskellthendefault_idelsetryId.of_string(Filename.basenamef)withUserError_->user_errPp.(str"Extraction: provided filename is not a valid identifier")inletf=ifFilename.is_relativefthenFilename.concat(output_directory())felsefinSome(f^d.file_suffix),Option.map((^)f)d.sig_suffix,id(* Builds a suitable filename from a module id *)letmodule_filenametablemp=letf=file_of_modfile(State.get_tabletable)mpinletid=Id.of_stringfinletf=Filename.concat(output_directory())finletd=descr()inletfimpl_base=d.file_namingtablemp^d.file_suffixinletfimpl=Filename.concat(output_directory())fimpl_baseinSomefimpl,Option.map((^)f)d.sig_suffix,id(*s Extraction of one decl to stdout. *)letprint_one_decltablestrucmpdecl=letd=descr()inlet()=State.resettableinlettable=State.set_phasetablePreinignore(d.pp_structtablestruc);lettable=State.set_phasetableImplinletans=State.with_visibilitytablemp[]beginfuntable->d.pp_decltabledeclendinv0ans(*s Extraction of a ml struct to a file. *)(** For Recursive Extraction, writing directly on stdout
won't work with rocqide, we use a buffer instead *)letformatterbufdryfile=letft=ifdrythenFormat.make_formatter(fun___->())(fun_->())elsematchfilewith|Somef->Topfmt.with_output_tof|None->Format.formatter_of_bufferbufin(* XXX: Fixme, this shouldn't depend on Topfmt *)(* We never want to see ellipsis ... in extracted code *)Format.pp_set_max_boxesftmax_int;(* We reuse the width information given via "Set Printing Width" *)(matchTopfmt.get_margin()with|None->()|Somei->Format.pp_set_marginfti;Format.pp_set_max_indentft(i-10));(* note: max_indent should be < margin above, otherwise it's ignored *)ftletget_comment()=lets=file_comment()inifString.is_emptysthenNoneelseletsplit_comment=Str.split(Str.regexp"[ \t\n]+")sinSome(prlist_with_sepspcstrsplit_comment)letprint_structure_to_filetable(fn,si,mo)drystruc=letbuf=Buffer.create1000inletd=descr()inlet()=State.resettableinletunsafe_needs={mldummy=struct_ast_searchMlutil.isMLdummystruc;tdummy=struct_type_searchMlutil.isTdummystruc;tunknown=struct_type_search((==)Tunknown)struc;magic=iflang()!=Haskellthenfalseelsestruct_ast_search(functionMLmagic_->true|_->false)struc}in(* First, a dry run, for computing objects to rename or duplicate *)lettable=State.set_phasetablePreinignore(d.pp_structtablestruc);letopened=opened_librariestablein(* Print the implementation *)letcout=ifdrythenNoneelseOption.mapopen_outfninletft=formatterbufdrycoutinletcomment=get_comment()inbegintry(* The real printing of the implementation *)lettable=State.set_phasetableImplinpp_withft(d.preambletablemocommentopenedunsafe_needs);pp_withft(d.pp_structtablestruc);Format.pp_print_flushft();Option.iterclose_outcout;withreraise->Format.pp_print_flushft();Option.iterclose_outcout;raisereraiseend;ifnotdrythenOption.iterinfo_filefn;(* Now, let's print the signature *)Option.iter(funsi->letcout=open_outsiinletft=formatterbuffalse(Somecout)inbegintrylettable=State.set_phasetableIntfinpp_withft(d.sig_preambletablemocommentopenedunsafe_needs);pp_withft(d.pp_sigtable(signature_of_structurestruc));Format.pp_print_flushft();close_outcout;withreraise->Format.pp_print_flushft();close_outcout;raisereraiseend;info_filesi)(ifdrythenNoneelsesi);(* Print the buffer content via Rocq standard formatter (ok with rocqide). *)ifnot(Int.equal(Buffer.lengthbuf)0)thenbeginFeedback.msg_notice(str(Buffer.contentsbuf));end(*********************************************)(*s Part III: the actual extraction commands *)(*********************************************)letinit?(inner=false)~modular~library()=ifnotinnerthencheck_inside_section();letkeywords=(descr()).keywordsinletstate=State.make~modular~library~keywords()inifmodular&&lang()==Schemethenerror_scheme();stateletwarnstable=lettable=State.get_tabletableinwarning_opaquestable(access_opaque());warning_axiomstable(* For a qualid, let's retrieve whether it corresponds
to a module or globref. If both, Warn the user and return the module. *)letlocate_refqid=letopenEitherinletmpo=trySome(Nametab.locate_moduleqid)withNot_found->Noneandro=tryletgr=Smartlocate.global_with_aliasqidinletinst=Environ.universes_of_global(Global.env())grinSome(List.map(funinst->{glob=gr;inst})(InfvInst.generateinst))withNametab.GlobalizationError_|UserError_->Noneinmatchmpo,rowith|None,None->Nametab.error_global_not_found~info:Exninfo.nullqid|None,Somer->Leftr|Somemp,None->Rightmp|Somemp,Somer->let()=warning_ambiguous_name?loc:qid.CAst.loc(qid,mp,(List.hdr).glob)inRightmpletlocate_refsl=letrefs,mods=List.partition_maplocate_reflinList.flattenrefs,mods(*s Recursive extraction in the Rocq toplevel. The vernacular command is
\verb!Recursive Extraction! [qualid1] ... [qualidn]. Also used when
extracting to a file with the command:
\verb!Extraction "file"! [qualid1] ... [qualidn]. *)letfull_extropaque_accessf(refs,mps)=lettable=init~modular:false~library:false()inList.iter(funmp->ifis_modfilempthenerror_MPfile_as_modmptrue)mps;letstruc=optimize_structtable(refs,mps)(mono_environmenttable~opaque_accessrefsmps)inlet()=warnstableinprint_structure_to_filetable(mono_filenamef)falsestrucletfull_extraction~opaque_accessflr=full_extropaque_accessf(locate_refslr)(*s Separate extraction is similar to recursive extraction, with the output
decomposed in many files, one per Rocq .v file *)letseparate_extraction~opaque_accesslr=lettable=init~modular:true~library:false()inletrefs,mps=locate_refslrinletstruc=optimize_structtable(refs,mps)(mono_environmenttable~opaque_accessrefsmps)inlet()=List.iter(function|MPfile_,_->()|(MPdot_|MPbound_),_->user_err(str"Separate Extraction from inside a module is not supported."))strucinlet()=warnstableinletprint=function|(MPfiledir,sel)ase->print_structure_to_filetable(module_filenametabledir)false[e]|(MPdot_|MPbound_),_->assertfalseinlet()=List.iterprintstrucin()(*s Simple extraction in the Rocq toplevel. The vernacular command
is \verb!Extraction! [qualid]. *)letsimple_extraction~opaque_accessr=matchlocate_refrwith|Rightmp->full_extropaque_accessNone([],[mp])|Left(r::_)->(* locate_ref returns all sort poly instantiations, should we extract all or just the Type one?
(currently just the Type one) *)lettable=init~modular:false~library:false()inletstruc=optimize_structtable([r],[])(mono_environmenttable~opaque_access[r][])inletd=get_decl_in_structurerstrucinlet()=warnstableinletflag=ifis_customrthenstr"(** User defined extraction *)"++fnl()elsemt()inletans=flag++print_one_decltablestruc(modpath_of_rr)dinFeedback.msg_noticeans|Left[]->assertfalse(*s (Recursive) Extraction of a library. The vernacular command is
\verb!(Recursive) Extraction Library! [M]. *)letextraction_library~opaque_accessis_recCAst.{loc;v=m}=lettable=init~modular:true~library:true()inletdir_m=(* XXX WTF is going on here? *)letq=qualid_of_identmintryNametab.full_name_moduleqwithNot_found->error_unknown_module?locqinletdir_m=dirpath_of_pathdir_minletvenv=Visit.make()inlet()=Visit.add_mp_allvenv(MPfiledir_m)inletenv=Global.env()inletl=List.rev(environment_until(Somedir_m))inletselectl(mp,struc)=ifVisit.needed_mpvenvmpthen(mp,extract_structuretableopaque_accessvenvenvmp(no_deltamp)~all:truestruc)::lelselinletstruc=List.fold_leftselect[]linletstruc=optimize_structtable([],[])strucinlet()=warnstableinletprint=function|(MPfiledir,sel)ase->letdry=notis_rec&¬(DirPath.equaldirdir_m)inprint_structure_to_filetable(module_filenametabledir)dry[e]|_->assertfalseinlet()=List.iterprintstrucin()(* For the test-suite :
extraction to a temporary file + run ocamlc on it *)letcompilef=tryletargs=["ocamlc";"-package";"zarith";"-I";Filename.dirnamef;"-c";f^"i";f]inletres=CUnix.sys_command(Boot.Env.ocamlfind())argsinmatchreswith|Unix.WEXITED0->()|Unix.WEXITEDn|Unix.WSIGNALEDn|Unix.WSTOPPEDn->CErrors.user_errPp.(str"Compilation of file "++strf++str" failed with exit code "++intn)withUnix.Unix_error(e,_,_)->CErrors.user_errPp.(str"Compilation of file "++strf++str" failed with error "++str(Unix.error_messagee))letremovef=ifSys.file_existsfthenSys.removefletextract_and_compile~opaque_accessl=iflang()!=OcamlthenCErrors.user_err(Pp.str"This command only works with OCaml extraction");letf=Filename.temp_file"testextraction"".ml"inlet()=full_extraction~opaque_access(Somef)linlet()=compilefinlet()=removef;remove(f^"i")inletbase=Filename.chop_suffixf".ml"inlet()=remove(base^".cmo");remove(base^".cmi")inFeedback.msg_notice(str"Extracted code successfully compiled")(* Show the extraction of the current ongoing proof *)letshow_extraction~pstate=lettable=init~inner:true~modular:false~library:false()inletprf=Declare.Proof.getpstateinletsigma,env=Declare.Proof.get_current_contextpstateinlettrms=Proof.partial_proofprfinletextr_termt=(* FIXME: substitute relevances with ground ones *)letast,ty=extract_constrtableenvsigmatinletmp=Lib.current_mp()inletl=Declare.Proof.get_namepstateinletfake_ref={glob=GlobRef.ConstRef(Constant.make2mpl);inst=InfvInst.empty}inletdecl=Dterm(fake_ref,ast,ty)inprint_one_decltable[]mpdeclinFeedback.msg_notice(Pp.prlist_with_sepPp.fnlextr_termtrms)