123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983(************************************************************************)(* * The Coq Proof Assistant / The Coq 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) *)(************************************************************************)(************************************************************************)(* SerAPI: Coq interaction protocol with bidirectional serialization *)(************************************************************************)(* Copyright 2016-2019 MINES ParisTech -- License LGPL 2.1+ *)(* Copyright 2019-2023 Inria -- License LGPL 2.1+ *)(* Written by: Emilio J. Gallego Arias and others *)(************************************************************************)openLtac_pluginopen!Sexplib.ConvmoduleExtra=structlethd_optl=matchlwith|[]->None|x::_->Somexletmemle=List.memel(* let sub s ~pos ~len = String.sub s pos len *)letvaluex~default=Option.defaultdefaultxletvalue_mapx~default~f=Option.catafdefaultxletis_prefix=Base.String.is_prefixletsplit_while=Base.List.split_while(* Custom tokenizer *)letrecstream_tokn_tokacclstr=lete=Gramlib.LStream.next(Pcoq.get_keyword_state())lstrinletloc=Gramlib.LStream.get_locn_toklstrinletl_tok=CAst.make~loceinifTok.(equaleEOI)thenList.rev(l_tok::acc)elsestream_tok(n_tok+1)(l_tok::acc)lstrend(******************************************************************************)(* SerAPI protocol & interpreter. *)(******************************************************************************)exceptionNoSuchStateofStateid.texceptionCannotSaveVo(******************************************************************************)(* Auxiliary Definitions *)(******************************************************************************)(******************************************************************************)(* Basic Protocol Objects *)(******************************************************************************)(* We'd like to use GADTs here, but we'd need to pack them somehow to
* support serialization both ways, see Jérémie's Dimino comment here:
*
* https://github.com/janestreet/ppx_sexp_conv/issues/8
*
* We need a type of tags + some packing, then:
*
* type _ object =
* | Option : option object
* | Search : string list object
* | Goals : goals object
* [@@deriving sexp]
*)(* XXX: Use a module here to have Coq.String etc...? *)typecoq_object=|CoqStringofstring|CoqSListofstringlist|CoqPpofPp.t(* | CoqRichpp of Richpp.richpp *)|CoqLocofLoc.t|CoqTokofTok.tCAst.tlist|CoqDPofNames.DirPath.t|CoqAstofVernacexpr.vernac_control|CoqOptionofGoptions.option_name*Goptions.option_state|CoqConstrofConstr.constr|CoqEConstrofEConstr.t|CoqExprofConstrexpr.constr_expr|CoqMIndofNames.MutInd.t*Declarations.mutual_inductive_body|CoqEnvofEnviron.env|CoqTacticofNames.KerName.t*Tacenv.ltac_entry|CoqLtacofTacexpr.raw_tactic_expr|CoqGenArgofGenarg.raw_generic_argument|CoqQualIdofLibnames.qualid|CoqGlobRefofNames.GlobRef.t|CoqGlobRefExtofGlobnames.extended_global_reference|CoqImplicitofImpargs.implicits_list|CoqProfDataofProfile_tactic.treenode|CoqNotationofConstrexpr.notation|CoqUnparsingofPpextend.notation_printing_rules*Notation_gram.notation_grammar|CoqGoalofConstr.tSerapi_goals.reified_goalSerapi_goals.ser_goals|CoqExtGoalofConstrexpr.constr_exprSerapi_goals.reified_goalSerapi_goals.ser_goals|CoqProofofEConstr.constrlist|CoqAssumptionsofSerapi_assumptions.t|CoqCommentsof((int*int)*string)listlist|CoqLibObjectsof{library_segment:Summary.Interp.frozenLib.library_segment;path_prefix:Nametab.object_prefix}(** Meta-logical Objects in Coq's library / module system *)(******************************************************************************)(* Printing Sub-Protocol *)(******************************************************************************)(* Basically every function here should be an straightforward call to
* Coq's printing. Coq bug if that is not the case.
*)letpp_goal_genpr_c{Serapi_goals.ty;hyp;_}=letopenPpinletpr_idlidl=prlist_with_sep(fun()->str", ")Names.Id.printidlinletpr_lconstr_optc=str" := "++pr_ccinletpr_hdef=Option.catapr_lconstr_opt(mt())inletpr_hyp(idl,hdef,htyp)=pr_idlidl++pr_hdefhdef++(str" : ")++pr_chtypinpr_vertical_listpr_hyphyp++str"============================\n"++(* (let pr_lconstr t = *)(* let (sigma, env) = Pfedit.get_current_context () in *)(* Ppconstr.Richpp.pr_lconstr_expr (Constrextern.extern_constr false env sigma t) *)(* in *)pr_ctyletpp_opt_value(s:Goptions.option_value)=matchswith|Goptions.BoolValueb->Pp.boolb|Goptions.IntValuei->Pp.pr_optPp.inti|Goptions.StringValues->Pp.strs|Goptions.StringOptValues->Pp.pr_optPp.strsletpp_optns=letopenPpinstr(String.concat"."n)++str" := "++pp_opt_values.Goptions.opt_value(* XXX fixme 8.15 *)let_pp_explicitation=letopenConstrexprinfunction|ExplByPosi->Pp.(inti++str"None")|ExplByNameiname->Names.Id.printinameletpp_implicit:Impargs.implicit_status->Pp.t=function|None->Pp.str"!"|Some_->Pp.str"!"(* pp_explicitation expl *)(*
let pp_richpp xml =
let open Xml_datatype in
let buf = Buffer.create 1024 in
let rec print = function
| PCData s -> Buffer.add_string buf s
| Element (_, _, cs) -> List.iter print cs
in
let () = print xml in
Buffer.contents buf
*)letgen_pp_objenvsigma(obj:coq_object):Pp.t=matchobjwith|CoqStrings->Pp.strs|CoqSLists->Pp.(pr_sequencestr)s|CoqPps->s(* | CoqRichpp s -> Pp.str (pp_richpp s) *)|CoqLoc_loc->Pp.mt()|CoqToktoks->Pp.pr_sequence(fun{CAst.v=tok;_}->Pp.str(Tok.(extract_stringfalsetok)))toks|CoqDPdp->Names.DirPath.printdp|CoqAstv->Ppvernac.pr_vernacv|CoqMInd(m,mind)->Printmod.pr_mutual_inductive_bodyenvmmindNone|CoqOption(n,s)->pp_optns|CoqConstrc->Printer.pr_lconstr_envenvsigmac|CoqEConstrc->Printer.pr_leconstr_envenvsigmac|CoqExpre->Ppconstr.pr_lconstr_exprenvsigmae|CoqEnv_env->Pp.str"Cannot pretty print environments"|CoqTactic(kn,_)->Names.KerName.printkn|CoqLtact->Pptactic.pr_raw_tacticenvsigmat|CoqGenArgga->letopenGenprintinbeginmatchgeneric_raw_printgawith|PrinterBasicpp->ppenvsigma(* XXX: Fixme, the level here is random *)|PrinterNeedsLevelpp->pp.printerenvsigmaConstrexpr.LevelSomeend(* Fixme *)|CoqGoalg->Pp.prlist_with_sepPp.fnl(pp_goal_genPrinter.(pr_lconstr_envenvsigma))g.Serapi_goals.goals|CoqExtGoalg->Pp.prlist_with_sepPp.fnl(pp_goal_genPpconstr.(pr_lconstr_exprenvsigma))g.Serapi_goals.goals|CoqProof_->Pp.str"FIXME UPSTREAM, provide pr_proof"|CoqProfData_pf->Pp.str"FIXME UPSTREAM, provide pr_prof_results"|CoqQualIdqid->Pp.str(Libnames.string_of_qualidqid)|CoqGlobRefgr->Printer.pr_globalgr|CoqGlobRefExtgr->(matchgrwith|Globnames.TrueGlobalgr->Printer.pr_globalgr|Globnames.Abbrevkn->Names.KerName.printkn)|CoqImplicit(_,l)->Pp.pr_sequencepp_implicitl|CoqNotationntn->Pp.str(sndntn)|CoqUnparsing_->Pp.str"FIXME Unparsing"|CoqAssumptionsa->Serapi_assumptions.printenvsigmaa(* | CoqPhyLoc(_,_,s)-> pr (Pp.str s) *)(* | CoqGoal (_,g,_) -> pr (Ppconstr.pr_lconstr_expr g) *)(* | CoqGlob g -> pr (Printer.pr_glob_constr g) *)|CoqComments_->Pp.str"FIXME comments"|CoqLibObjects_->Pp.str"FIXME libobjects"letstr_pp_objenvsigmafmt(obj:coq_object)=Format.fprintffmt"%a"Pp.pp_with(gen_pp_objenvsigmaobj)(** Print output format *)typeprint_format=|PpSer(** Output in serialized format [usually sexp but could be extended in the future] *)|PpStr(** Output a string with a human-friendly representation *)|PpTex(** Output a TeX expression *)|PpCoq(** Output a TeX expression *)(* | PpRichpp *)(* register printer *)typeformat_opt={pp_format:print_format[@defaultPpSer];pp_depth:int[@default0];pp_elide:string[@default"..."];pp_margin:int[@default72];}typeprint_opt={sid:Stateid.t[@defaultStm.get_current_state~doc:Stm.(get_doc0)];(** [sid] denotes the {e sentence id} we are querying over, essential information as goals for example will vary. *)pp:format_opt[@default{pp_format=PpSer;pp_depth=0;pp_elide="...";pp_margin=72}];(** Printing format of the query, this can be used to select the type of the answer, as for example to show goals in human-form. *)}letpp_tex(_obj:coq_object)=""(*
let tex_sexp c = let open Format in
pp_set_margin str_formatter 300;
pp_set_max_indent str_formatter 300;
Stexp.pp_sexp_to_tex str_formatter c;
flush_str_formatter ()
in
let open List in
let open Ser_constr in
let open Ser_constrexpr in
let open Ser_vernacexpr in
let open Serapi_goals in
match obj with
| CoqConstr cst -> sexp_of_constr cst |> tex_sexp
| CoqGoal gl -> let cst = (hd gl.goals).ty in
sexp_of_constr cst |> tex_sexp
| CoqExtGoal gl -> let cst = (hd gl.goals).ty in
sexp_of_constr_expr cst |> tex_sexp
| CoqAst(_,ast) -> sexp_of_vernac_control ast |> tex_sexp
| _ -> "not supported"
*)letobj_printenvsigmapr_optobj=letopenFormatinmatchpr_opt.pp_formatwith|PpSer->obj|PpCoq->CoqPp(gen_pp_objenvsigmaobj)|PpTex->CoqString(pp_texobj)(* | PpRichpp -> CoqRichpp (Richpp.richpp_of_pp pr_opt.pp_margin (gen_pp_obj obj)) *)|PpStr->letmb=pp_get_max_boxesstr_formatter()inletet=pp_get_ellipsis_textstr_formatter()inletmg=pp_get_marginstr_formatter()inpp_set_max_boxesstr_formatterpr_opt.pp_depth;pp_set_ellipsis_textstr_formatterpr_opt.pp_elide;pp_set_marginstr_formatterpr_opt.pp_margin;fprintfstr_formatter"@[%a@]"(str_pp_objenvsigma)obj;letstr_obj=CoqString(flush_str_formatter())inpp_set_max_boxesstr_formattermb;pp_set_ellipsis_textstr_formatteret;pp_set_marginstr_formattermg;str_obj(******************************************************************************)(* Answer Types *)(******************************************************************************)moduleExnInfo=structtypet={loc:Loc.toption;stm_ids:(Stateid.t*Stateid.t)option;backtrace:Printexc.raw_backtrace;exn:exn;pp:Pp.t;str:string}endtypeanswer_kind=Ack|Completed|AddedofStateid.t*Loc.t*Stm.add_focus|CanceledofStateid.tlist|ObjListofcoq_objectlist|CoqExnofExnInfo.t(******************************************************************************)(* Query Sub-Protocol *)(******************************************************************************)(** Max number of results to return, 0 will return a summary *)(* type query_limit = int option *)(** Filtering predicates *)typequery_pred=|Prefixofstring(* Filter by type *)(* Filter by module *)letprefix_pred(prefix:string)(obj:coq_object):bool=matchobjwith|CoqStrings->Extra.is_prefixs~prefix|CoqSList_->true(* XXX *)|CoqLoc_->true|CoqTok_->true|CoqPp_->true(* | CoqRichpp _ -> true *)|CoqAst_->true|CoqDP_->true|CoqOption(n,_)->Extra.is_prefix(String.concat"."n)~prefix|CoqConstr_->true|CoqEConstr_->true|CoqExpr_->true|CoqMInd_->true|CoqEnv_->true|CoqTactic(kn,_)->Extra.is_prefix(Names.KerName.to_stringkn)~prefix|CoqLtac_->true|CoqGenArg_->true(* | CoqPhyLoc _ -> true *)|CoqQualId_->true|CoqGlobRef_->true|CoqGlobRefExt_->true|CoqProfData_->true|CoqImplicit_->true|CoqGoal_->true|CoqNotation_->true|CoqUnparsing_->true|CoqExtGoal_->true|CoqProof_->true|CoqAssumptions_->true|CoqComments_->true|CoqLibObjects_->trueletgen_pred(p:query_pred)(obj:coq_object):bool=matchpwith|Prefixs->prefix_predsobjtypequery_opt={preds:query_predlist[@sexp.list];limit:intoption[@sexp.option];sid:Stateid.t[@defaultStm.get_current_state()];pp:format_opt[@default{pp_format=PpSer;pp_depth=0;pp_elide="...";pp_margin=72}];(* Legacy/Deprecated *)route:Feedback.route_id[@default0];}(** XXX: This should be in sync with the object tag! *)typequery_cmd=|Option(* *)|Search(* Search vernacular, we only support prefix by name *)|Goals(* Return goals [TODO: Add filtering/limiting options] *)|EGoals(* Return goals [TODO: Add filtering/limiting options] *)|Ast(* Return ast *)|TypeOfofstring|Namesofstring(* XXX Move to prefix *)|Tacticsofstring(* XXX Print LTAC signatures (with prefix) *)|Locateofstring(* XXX Print LTAC signatures (with prefix) *)|Implicitsofstring(* XXX Print LTAC signatures (with prefix) *)|Unparsingofstring(* XXX *)|Definitionofstring|LogicalPathofstring|PNotations(* XXX *)|ProfileData|Proof(* Return the proof object *)|Vernacofstring(* [legacy] Execute arbitrary Coq command in an isolated state. *)|Env(* Return the current global enviroment *)|Assumptionsofstring(* Return the assumptions of given identifier *)|Completeofstring|Comments(** Get all comments of a document *)|Objects(** Get Coq meta-logical module objects *)moduleQueryUtil=structlet_query_namesprefix=letacc=ref[]inletenv=Global.env()inletsigma=Evd.from_envenvinSearch.generic_searchenvsigma(fungr_kind_env_sigma_typ->(* Not happy with this at ALL:
String of qualid is OK, but shortest_qualid_of_global is an
unacceptable round-trip. I don't really see other option
than to maintain a prefix-specific table on the Coq side
capturing all the possible aliases.
*)letname=Libnames.string_of_qualid(Nametab.shortest_qualid_of_globalNames.Id.Set.emptygr)inifExtra.is_prefixname~prefixthenacc:=name::!acc);[CoqSList!acc]letquery_names_locateprefix=letall_gr=Nametab.locate_all@@Libnames.qualid_of_ident(Names.Id.of_stringprefix)inList.map(funx->CoqGlobRefx)all_gr(* From @ppedrot *)(* XXX: We need to put this into a plugin, as ltac is now a plugin in 8.7. *)letquery_tacticsprefix=letopenNamesinletprefix_longkn=Extra.is_prefix(KerName.to_stringkn)~prefixinletprefix_bestkn=tryExtra.is_prefix(Libnames.string_of_qualid(Tacenv.shortest_qualid_of_tactickn))~prefixwithNot_found->(* Debug code, It is weird that shortest_qualid_of_tactic returns a Not_found... :S *)(* Format.eprintf "%s has no short name@\n%!" (KerName.to_string kn); *)falseinlettpredkn_=prefix_longkn||prefix_bestkninKNmap.bindings@@KNmap.filtertpred@@Tacenv.ltac_entries()[@@warning"-44"](* let map (kn, entry) = *)(* let qid = *)(* try Some (Nametab.shortest_qualid_of_tactic kn) *)(* with Not_found -> None *)(* in *)(* match qid with *)(* | None -> None *)(* | Some qid -> Some (qid, entry.Tacenv.tac_body) *)(* in *)(* List.map map entries [] *)letquery_unparsing(nt:Constrexpr.notation):Ppextend.notation_printing_rules*Notation_gram.notation_grammar=Ppextend.find_notation_printing_ruleNonent,Notgram_ops.grammar_of_notationntletquery_pnotations()=Notgram_ops.get_defined_notations()letlocateid=letopenNamesinletopenLibnamesinletopenGlobnamesin(* From prettyp.ml *)letqid=qualid_of_stringidinletexpand=function|TrueGlobalref->Nametab.shortest_qualid_of_globalId.Set.emptyref|Abbrevkn->Nametab.shortest_qualid_of_abbreviationId.Set.emptykninList.mapexpand(Nametab.locate_extended_allqid)letimplicitsid=letopenNamesinletopenLibnamesintryletref=Nametab.locate(qualid_of_ident(Id.of_stringid))inImpargs.implicits_of_globalrefwithNot_found->[](* Copied from Coq. XXX *)lettype_of_constantcb=cb.Declarations.const_type(* Definition of an inductive *)letinfo_of_indenv(sp,_)=[CoqMInd(sp,Environ.lookup_mindspenv)],[]letinfo_of_constenvcr=letcdef=Environ.lookup_constantcrenvinletcb=Environ.lookup_constantcrenvinOption.cata(fun(cb,_univs,_uctx)->[CoqConstrcb])[](Global.body_of_constant_body(Library.indirect_accessor[@warning"-3"])cb),[CoqConstr(type_of_constantcdef)]letinfo_of_varenvvr=letvdef=Environ.lookup_namedvrenvinOption.cata(funcb->[CoqConstrcb])[](Context.Named.Declaration.get_valuevdef),[CoqConstr(Context.Named.Declaration.get_typevdef)](* XXX: Some work to do wrt Global.type_of_global_unsafe *)letinfo_of_constructorenvcr=(* let cdef = Global.lookup_pinductive (cn, cu) in *)let(ctype,_uctx)=Typeops.type_of_global_in_contextenv(Names.GlobRef.ConstructRefcr)in[],[CoqConstrctype](* Queries a generic definition, in the style of the `Print` vernacular *)(* definition type *)letinfo_of_idenvid:coq_objectlist*coq_objectlist=(* parse string to a qualified name *)letqid=Libnames.qualid_of_stringidin(* try locate the kind of object the name refers to *)tryletlid=Nametab.locateqidin(* dispatch based on type *)letopenNames.GlobRefinmatchlidwith|VarRefvr->info_of_varenvvr|ConstRefcr->info_of_constenvcr|IndRefir->info_of_indenvir|ConstructRefcr->info_of_constructorenvcrwith_->[],[]letassumptionsenvid=letqid=Libnames.qualid_of_stringidinletsmart_globalr=letgr=Nametab.locaterinDumpglob.add_glob?loc:r.locgr;grinletgr=smart_globalqidin(* Assumptions doesn't care about the universes *)letcstr,_=UnivGen.fresh_global_instanceenvgrinletst=Conv_oracle.get_transp_state(Environ.oracleenv)inletnassums=Assumptions.assumptions(Library.indirect_accessor[@warning"-3"])st~add_opaque:true~add_transparent:truegrcstrinSerapi_assumptions.buildenvnassums(* This should be moved Coq upstream *)let_comments=ref[]letadd_commentspa=letcomments=Pcoq.Parsable.commentspa|>List.revin_comments:=comments::!_commentsletlibobjects()=letlibrary_segment=Lib.contents()inletpath_prefix=Lib.prefix()inCoqLibObjects{library_segment;path_prefix}letget_proof~pstate=matchpstatewith|Somepstate->letpterms=Declare.Proof.getpstate|>Proof.partial_proofinList.map(funx->CoqEConstrx)pterms|None->[]endletobj_query~doc~pstate~env(opt:query_opt)(cmd:query_cmd):coq_objectlist=matchcmdwith|Option->lettable=Goptions.get_tables()inletopts=Goptions.OptionMap.bindingstableinList.map(fun(n,s)->CoqOption(n,s))opts|Goals->Option.cata(fung->[CoqGoalg])[]@@Serapi_goals.get_goals~docopt.sid|EGoals->Option.cata(fung->[CoqExtGoalg])[]@@Serapi_goals.get_egoals~docopt.sid|Ast->Option.cata(funlast->[CoqAstlast])[]@@Stm.get_ast~docopt.sid|Namesprefix->QueryUtil.query_names_locateprefix|Tacticsprefix->List.map(fun(i,t)->CoqTactic(i,t))@@QueryUtil.query_tacticsprefix|Locateid->List.map(funqid->CoqQualIdqid)@@QueryUtil.locateid|Implicitsid->List.map(funii->CoqImplicitii)@@QueryUtil.implicitsid|ProfileData->[CoqProfData(Profile_tactic.get_local_profiling_results())]|Proof->QueryUtil.get_proof~pstate|Unparsingntn->(* Unfortunately this will produce an anomaly if the notation is not found...
* To keep protocol promises we need to special wrap it.
*)begintryletup,gr=QueryUtil.query_unparsing(Constrexpr.InConstrEntry,ntn)in[CoqUnparsing(up,gr)]with_exn->[]end|LogicalPathf->[CoqDP(Serapi_paths.dirpath_of_filef)]|PNotations->List.map(funs->CoqNotations)@@QueryUtil.query_pnotations()|Definitionid->fst(QueryUtil.info_of_idenvid)|TypeOfid->snd(QueryUtil.info_of_idenvid)|Search->[CoqString"Not Implemented"](* XXX: should set printing options in all queries *)|Vernacq->letpa=Pcoq.Parsable.make(Gramlib.Stream.of_stringq)inStm.query~doc~at:opt.sid~route:opt.routepa;[](* XXX: Should set the proper sid state *)|Env->[CoqEnvenv]|Assumptionsid->[CoqAssumptionsQueryUtil.(assumptionsenvid)]|Completeprefix->List.map(funx->CoqGlobRefExtx)(Nametab.completion_canditates(Libnames.qualid_of_stringprefix))|Comments->[CoqComments(List.rev!QueryUtil._comments)]|Objects->[QueryUtil.libobjects()]letobj_filterpredsobjs=List.(fold_left(funobjp->filter(gen_predp)obj)objspreds)(* XXX: OCaml! .... *)letrectakenl=ifn=0then[]elsematchlwith|[]->[]|x::xs->x::take(n-1)xsletobj_limitlimitobjs=matchlimitwith|None->objs|Somen->takenobjs(* XXX: Need to protect queries... sad *)letdoc_id=ref0(* XXX: Needs to take into account possibly local proof state *)letproof_state_of_stm=matchmwith|Stm.Valid(Some{Vernacstate.interp={lemmas;_};_})->lemmas|_->Noneletparsing_state_of_stm=matchmwith|Stm.Valid(Some{Vernacstate.synterp;_})->Some(Vernacstate.Synterp.parsingsynterp)|_->Noneletcontext_of_stm=matchmwith|Stm.Valid(Some{Vernacstate.interp={lemmas=Somepstate;_};_})->Vernacstate.LemmaStack.with_toppstate~f:Declare.Proof.get_current_context|_->letenv=Global.env()inEvd.from_envenv,envletexec_queryoptcmd=letdoc=Stm.get_doc!doc_idinletst=Stm.state_of_id~docopt.sidinletsigma,env=context_of_ststinletpstate=proof_state_of_ststinletpstate=Option.map(Vernacstate.LemmaStack.with_top~f:(funp->p))pstateinletres=obj_query~doc~pstate~envoptcmdin(* XXX: Filter should move to query once we have GADT *)letres=obj_filteropt.predsresinletres=obj_limitopt.limitresinList.map((obj_printenvsigma)opt.pp)res(******************************************************************************)(* Control Sub-Protocol *)(******************************************************************************)(* coq_exn info *)letcoq_exn_infoexn=letbacktrace=Printexc.get_raw_backtrace()inletexn,info=Exninfo.captureexninletpp=CErrors.iprint(exn,info)inCoqExn{loc=Loc.get_locinfo;stm_ids=Stateid.getinfo;backtrace;exn;pp;str=Pp.string_of_ppcmdspp}(* Simple protection for Coq-generated exceptions *)letcoq_protectste=trye()@[Completed],stwithexn->[coq_exn_infoexn;Completed],st(* let msg = str msg ++ fnl () ++ CErrors.print ~info e in *)(* Richpp.richpp_of_pp msg *)typeparse_entry=Vernac|Constr(** parse [ontop] of the given sentence with entry [entry] *)typeparse_opt={ontop:Stateid.toption[@sexp.option];entry:parse_entry[@defaultVernac]}typeadd_opts={lim:intoption[@sexp.option];ontop:Stateid.toption[@sexp.option];newtip:Stateid.toption[@sexp.option];verb:bool[@defaultfalse];}moduleControlUtil=structtypedoc=Stateid.tlistletcur_doc:docref=ref[Stateid.of_int1]letpp_docfmtl=letopenSerapi_ppinFormat.fprintffmt"@[%a@]"(pp_list~sep:" "pp_stateid)llet_dump_doc()=Format.eprintf"%a@\n%!"pp_doc!cur_docletparse_expr~doc~ontopstr=letontop=Extra.valueontop~default:(Stm.get_current_state~doc)inparsing_state_of_st(Stm.state_of_id~docontop)|>Option.map(funpstate->letentry=Pcoq.Constr.lconstrinletpa=Pcoq.Parsable.make(Gramlib.Stream.of_stringstr)inPcoq.unfreezepstate;Pcoq.Entry.parseentrypa)letparse_sentence~doc~ontopsent=letontop=Extra.valueontop~default:(Stm.get_current_state~doc)inletpa=Pcoq.Parsable.make(Gramlib.Stream.of_stringsent)inletentry=Pvernac.main_entryinStm.parse_sentence~docontop~entrypaletparse_entry~doc~(opt:parse_opt)str=letontop=opt.ontopinmatchopt.entrywith|Vernac->Option.map(funx->[CoqAstx])(parse_sentence~doc~ontopstr)|Constr->Option.map(funx->[CoqExprx])(parse_expr~doc~ontopstr)exceptionEnd_of_inputletadd_sentences~docoptssent=letpa=Pcoq.Parsable.make(Gramlib.Stream.of_stringsent)inleti=ref1inletacc=ref[]inletstt=ref(Extra.valueopts.ontop~default:(Stm.get_current_state~doc))inletdoc=refdocinletcheck_limi=Extra.value_mapopts.lim~default:true~f:(funlim->i<=lim)intrywhilecheck_lim!ido(* XXX: We check that the ontop state is actually in the
* document to avoid an Anomaly exception.
*)ifnot(List.mem!stt!cur_doc)thenraise(NoSuchState!stt);leteast=(* Flags.beautify is needed so comments are stored by the lexer... *)letparse_res=Flags.with_optionFlags.beautify(Stm.parse_sentence~doc:!doc!stt~entry:Pvernac.main_entry)painmatchparse_reswith|Someast->ast|None->raiseEnd_of_inputinFlags.beautify:=false;(* XXX: Must like refine the API *)leteloc=Option.get(east.CAst.loc)inletn_doc,n_st,foc=Stm.add~doc:!doc?newtip:opts.newtip~ontop:!sttopts.verbeastindoc:=n_doc;cur_doc:=n_st::!cur_doc;acc:=(Added(n_st,eloc,foc))::!acc;stt:=n_st;incridone;!doc,pa,List.rev!accwith|End_of_input->!doc,pa,List.rev!acc|exn->!doc,pa,List.rev(coq_exn_infoexn::!acc)(* We follow a suggestion by Clément to report sentence invalidation
in a more modular way: When we issue the cancel command, we will
look for the cancelled part
*)letcancel_intervalst(foc:Stm.focus)=letopenSerapi_ppinletfmt=Format.err_formatterinFormat.fprintffmt"Cancel interval: [%a -- %a]"pp_stateidstpp_stateidfoc.Stm.stop;[](* eprintf "%d" foc.stop *)(* failwith "SeqAPI FIXME, focus not yet supported" *)(* We recover the list of states to cancel plus the first valid
one. The main invariant is that:
- The state has to belong to the list.
- The we cancel states that are newer
*)letinvalid_rangecan_st~incl:include_st=letpredst=ifinclude_stthenStateid.newer_thanstcan_st||Stateid.equalstcan_stelseStateid.newer_thanstcan_stinifExtra.mem!cur_doccan_stthenExtra.split_while!cur_doc~f:predelse[],!cur_docletcancel_sentence~doccan_st=(* dump_doc (); *)letc_ran,k_ran=invalid_rangecan_st~incl:trueinletprev_st=Extra.value(Extra.hd_optk_ran)~default:Stateid.initialinmatchStm.edit_at~docprev_stwith|doc,NewTip->cur_doc:=k_ran;doc,[Canceledc_ran](* - [tip] is the new document tip.
- [st] -- [stop] is dropped.
- [stop] -- [tip] has been kept.
- [start] is where the editing zone starts
- [add] happen on top of [id].
*)|doc,Focusfoc->doc,cancel_intervalcan_stfocend(******************************************************************************)(* Init / new document *)(******************************************************************************)typenewdoc_opts={top_name:Coqargs.top(** name of the top-level module of the new document *);ml_load_path:stringlistoption[@sexp.option](** Initial ML loadpath *);vo_load_path:Loadpath.vo_pathlistoption[@sexp.option](** Initial LoadPath for the document *)(* [XXX: Use the coq_pkg record?] *);require_libs:Coqargs.require_injectionlistoption[@sexp.option](** Libraries to load in the initial document state *)}(** Save options, Coq must save a module `Foo` to a concrete module
path determined by -R / -Q options , so we don't have a lot of
choice here. *)typesave_opts={prefix_output_dir:stringoption[@sexp.option](** prefix a directory to the saved vo file. *);sid:Stateid.t(** sid of the point to save the document *)}(******************************************************************************)(* Help *)(******************************************************************************)(* Prints help to stderr. TODO, we should use a ppx to automatically
generate the description of the protocol. *)letserproto_help()=letopenFormatineprintf"%s%!"("Coq SerAPI -- Protocol documentation is still incomplete, the main commands are: \n\n"^" (Add add_opt \"gallina code\") -- Add new sentences to the current document \n"^" (Cancel sid_list) -- Cancel sentences in the current document \n"^" (Exec sid) -- Check sentence `sid` \n"^" (Query query_opt query_cmd) -- Query information about a sentence / global data \n"^" (Print print_opt coq_object) -- Print object with options \n"^"\nSee sertop_protocol.mli for more details.\n\n")(******************************************************************************)(* State *)(******************************************************************************)moduleState=structtypet={in_file:stringoption;ldir:Names.DirPath.toption}letmake?in_file?ldir()={in_file;ldir}end(******************************************************************************)(* Top-Level Commands *)(******************************************************************************)typecmd=|NewDocofnewdoc_opts|SaveDocofsave_opts|Addofadd_opts*string|CancelofStateid.tlist|ExecofStateid.t|Queryofquery_opt*query_cmd|Printofprint_opt*coq_object|Parseofparse_opt*string(* Full document checking *)|Join|Finish(*******************************************************************)(* Non-supported commands, only for convenience. *)|ReadFileofstring|Tokenizeofstring(*******************************************************************)(* Administrativia *)|Noop|Help(*******************************************************************)letexec_cmd(st:State.t)(cmd:cmd):answer_kindlist*State.t=letdoc=Stm.get_doc!doc_idincoq_protectst@@fun()->matchcmdwith|NewDocopts->letstm_options=Stm.AsyncOpts.default_optsinletrequire_libs=Option.default[{Coqargs.lib="Coq.Init.Prelude";prefix=None;export=SomeLib.Export;}]opts.require_libsinStm.init_processstm_options;letndoc={Stm.doc_type=Stm.(Interactiveopts.top_name);injections=List.map(funx->Coqargs.RequireInjectionx)require_libs}in(* This got broken upstream :S *)(* doc_id := fst Stm.(new_doc ndoc); [] *)let_=Stm.new_docndocindoc_id:=0;[]|SaveDocopts->beginmatchst.in_filewith|None->raiseCannotSaveVo|Somein_file->letin_file=Option.cata(funprefix->Filename.concatprefixin_file)in_fileopts.prefix_output_dirinlet_doc=Stm.observe~docopts.sidinletpst=Stm.state_of_id~docopts.sidinletpstate=proof_state_of_stpstinSerapi_doc.save_vo~doc~pstate~in_file?ldir:st.ldir();[]end|Add(opt,s)->let_doc,pa,res=ControlUtil.add_sentences~docoptsinQueryUtil.add_commentspa;res|Cancelstms->List.concat@@List.map(funx->snd@@ControlUtil.(cancel_sentence~docx))stms|Execst->ignore(Stm.observe~docst);[]|Query(opt,qry)->[ObjList(exec_queryoptqry)]|Print(opts,obj)->letst=Stm.state_of_id~docopts.sidinletsigma,env=context_of_ststin[ObjList[obj_printenvsigmaopts.ppobj]]|Parse(opt,s)->ControlUtil.(parse_entry~doc~opts)|>Option.cata(funobjs->[ObjListobjs])[]|Join->ignore(Stm.join~doc);[]|Finish->ignore(Stm.finish~doc);[](* *)|ReadFilef->(letinc=open_infintryletfaddopts={lim=None;ontop=None;newtip=None;verb=false;}inletfsize=in_channel_lengthincinletfcontent=really_input_stringincfsizeinlet_doc,pa,res=ControlUtil.add_sentences~docfaddoptsfcontentinQueryUtil.add_commentspa;reswith_->close_ininc;[])|Tokenizeinput->letst=CLexer.Lexer.State.get()inbegintryletistr=Gramlib.Stream.of_stringinputinletlex=CLexer.Lexer.tok_funcistrinCLexer.Lexer.State.setst;letobjs=Extra.stream_tok0[]lexin[ObjList[CoqTokobjs]]withexn->CLexer.Lexer.State.setst;raiseexnend|Help->serproto_help();[]|Noop->[]typecmd_tag=stringtypetagged_cmd=cmd_tag*cmdtypefeedback_content=|Processed|Incomplete|Complete|ProcessingInofstring|InProgressofint|WorkerStatusofstring*string|AddedAxiom|FileDependencyofstringoption*string|FileLoadedofstring*string|Messageof{level:Feedback.level;loc:Loc.toption;pp:Pp.t;str:string}typefeedback={doc_id:Feedback.doc_id;span_id:Stateid.t;route:Feedback.route_id;contents:feedback_content}typeanswer=|Answerofcmd_tag*answer_kind|Feedbackoffeedback