123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)(* <O___,, * (see CREDITS file for the list of authors) *)(* \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) *)(************************************************************************)(************************************************************************)(* Coq serialization API/Plugin *)(* Copyright 2016-2019 MINES ParisTech -- Dual License LGPL 2.1 / GPL3+ *)(* Copyright 2019-2021 Inria -- Dual License LGPL 2.1 / GPL3+ *)(* Written by: Emilio J. Gallego Arias *)(************************************************************************)(* Status: Very Experimental *)(************************************************************************)(* Cost analysis: in the past we used to pay almost 3MiB of overload
* in SerAPI.
*
* Now, thanks to smarter linking flags we are paying ~500k for the whole
* of SerTOP when compared to jsCoq, it is IMHO great.
*
* Unfortunately, Core_kernel would cost us ~1.8MiB more, it is too
* much for now until we figure out a better linking strategy. We manually
* embed a few utility functions in the `Extra` module below.
*)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.catafdefaultx(******************************************************************************)(* Taken from Core_kernel, (c) Jane Street, released under Apache License 2.0 *)letis_prefix_gen=letrecloops~prefix~char_equali=i<0||((char_equalprefix.[i]s.[i])&&loops~prefix~char_equal(i-1))infuns~prefix~char_equal->letprefix_len=String.lengthprefixinString.lengths>=prefix_len&&loops~prefix~char_equal(prefix_len-1)letis_prefixs~prefix=is_prefix_gens~prefix~char_equal:(funxy->x=y)letsplit_whilexs~f=letrecloopacc=function|hd::tlwhenfhd->loop(hd::acc)tl|t->(List.revacc,t)inloop[]xs(* End of Core_kernel code, (c) Jane Street *)(******************************************************************************)letrecstream_tokn_tokacclstr=lete=LStream.nextlstrinletloc=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|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_ltac.treenode|CoqNotationofConstrexpr.notation|CoqUnparsingofPpextend.unparsing_rule*Ppextend.extra_unparsing_rules*Notation_gram.notation_grammar|CoqGoalofConstr.tSerapi_goals.reified_goalSerapi_goals.ser_goals|CoqExtGoalofConstrexpr.constr_exprSerapi_goals.reified_goalSerapi_goals.ser_goals|CoqProofofGoal.goallist*(Goal.goallist*Goal.goallist)list(* We don't seralize the evar map for now... *)(* * Evd.evar_map *)|CoqAssumptionsofSerapi_assumptions.t|CoqCommentsof((int*int)*string)listlist(******************************************************************************)(* 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_valueletpp_explicitation=letopenConstrexprinfunction|ExplByPos(i,None)->Pp.(inti++str"None")|ExplByPos(i,Someiname)->Pp.(inti++Names.Id.printiname)|ExplByNameiname->Names.Id.printinameletpp_implicit:Impargs.implicit_status->Pp.t=function|None->Pp.str"!"|Some(expl,_,_)->pp_explicitationexpl(*
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|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.SynDefkn->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"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*[`NewTip|`UnfocusofStateid.t]|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|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_->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 *)moduleQueryUtil=structlet_query_namesprefix=letacc=ref[]inletenv=Global.env()inSearch.generic_searchenv(fungr_kind_env_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.unparsing_rule*Ppextend.extra_unparsing_rules*Notation_gram.notation_grammar=Ppextend.find_notation_printing_ruleNonent,Ppextend.find_notation_extra_printing_rulesNonent,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|SynDefkn->Nametab.shortest_qualid_of_syndefId.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_bodyLibrary.indirect_accessorcb),[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_globalqidinletcstr=Globnames.printable_constr_of_globalgrinletst=Conv_oracle.get_transp_state(Environ.oracleenv)inletnassums=Assumptions.assumptionsst~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::!_commentsendletobj_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_ltac.get_local_profiling_results())]|Proof->Option.cata(funpstate->letProof.{goals;stack;_}=Proof.data(Declare.Proof.getpstate)in[CoqProof(goals,stack)])[]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,upe,gr=QueryUtil.query_unparsing(Constrexpr.InConstrEntry,ntn)in[CoqUnparsing(up,upe,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(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)]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 *)letpstate_of_stm=matchmwith|`Valid(Some{Vernacstate.lemmas;_})->Option.map(Vernacstate.LemmaStack.with_top~f:(funp->p))lemmas|_->Noneletcontext_of_stm=matchmwith|`Valid(Some{Vernacstate.lemmas=Somelemma;_})->Vernacstate.LemmaStack.with_toplemma~f:(funp->Declare.Proof.get_current_contextp)(* let pstate = st.Vernacstate.proof in *)(* let summary = States.summary_of_state st.Vernacstate.system in
* Safe_typing.env_of_safe_env Summary.(project_from_summary summary Global.global_env_summary_tag) *)|_->letenv=Global.env()inEvd.from_envenv,envletexec_queryoptcmd=letdoc=Stm.get_doc!doc_idinletst=Stm.state_of_id~docopt.sidinletsigma,env=context_of_ststinletpstate=pstate_of_ststinletres=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_opt={ontop:Stateid.toption[@sexp.option]}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_sentence~doc~(opt:parse_opt)sent=letontop=Extra.valueopt.ontop~default:(Stm.get_current_state~doc)inletpa=Pcoq.Parsable.make(Stream.of_stringsent)inletentry=Pvernac.main_entryinStm.parse_sentence~docontop~entrypaexceptionEnd_of_inputletadd_sentences~docoptssent=letpa=Pcoq.Parsable.make(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:(string*stringoption*booloption)listoption[@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(["Coq.Init.Prelude",None,Sometrue])opts.require_libsinletndoc={Stm.doc_type=Stm.(Interactiveopts.top_name);injections=List.map(funx->Coqargs.RequireInjectionx)require_libs;stm_options}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=pstate_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)->Option.cata(funast->[ObjList[CoqAstast]])[]ControlUtil.(parse_sentence~doc~opts)|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=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