123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201(******************************************************************************)(* This file is part of Waterproof-lib. *)(* *)(* Waterproof-lib is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU General Public License as published by *)(* the Free Software Foundation, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* Waterproof-lib 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 General Public License for more details. *)(* *)(* You should have received a copy of the GNU General Public License *)(* along with Waterproof-lib. If not, see <https://www.gnu.org/licenses/>. *)(* *)(******************************************************************************)openHintsopenPpopenProofviewopenProofview.NotationsopenBacktracking(**
Returns the index of the first element [x] of [l] such that `f x` is [true]
*)letfind_first(f:'a->bool)(l:'alist):intoption=letrecaux(index:int)(p:'alist):intoption=matchpwith|[]->None|x::qwhenfx->Someindex|_::q->aux(index+1)qinaux0l(**
Returns the index of the last element [x] of [l] such that `f x` is [true]
*)letfind_last(f:'a->bool)(l:'alist):intoption=letrecaux(current_index:int)(last_index:intoption)(p:'alist):intoption=matchpwith|[]->last_index|x::q->aux(current_index+1)(iffxthenSomecurrent_indexelselast_index)qinaux0Nonel(**
Returns the queue of the given list after the [n]th element included
*)letrectail_end(l:'alist)(n:int):'alist=match(l,n)with|(_,0)->l|([],_)->[]|(_::p,_)->tail_endp(n-1)(**
Generic dictionnary taking strings as keys
*)moduleStringMap=Map.Make(String)(** Maps the given function to the list then applies every returned tactic *)lettclMAP_rev(f:'a->unittactic)(args:'alist):unittactic=List.fold_left(funaccuarg->Tacticals.tclTHENaccu(farg))(Proofview.tclUNIT())args(** Generic mergeable type *)moduletypeMergeable=sig(** Type of the elements *)typeelt(** Empty value *)valempty:elt(** How to merge two elements *)valmerge:elt->elt->eltend(** Generalization of tactics defined in coq-core for {! Mergeable}-typed tactics *)moduleTypedTactics(M:Mergeable)=struct(** Merge of tactics' returned elements *)lettypedThen(tactic1:M.elttactic)(tactic2:M.elttactic):M.elttactic=tactic1>>=funelt1->tactic2>>=funelt2->tclUNIT@@M.mergeelt1elt2(** Same as {! typedThen} with a list of tactics *)lettypedLongThen(tactics:M.elttacticlist):M.elttactic=List.fold_lefttypedThen(tclUNITM.empty)tactics(** Generalization of {! Proofview.Goal.enter} *)lettypedGoalEnter(f:Goal.t->M.elttactic):M.elttactic=Goal.goals>>=fungoals->lettactics=List.map(fungoal_tactic->goal_tactic>>=f)goalsinList.fold_left(funacctac->typedThenacctac)(tclUNITM.empty)tactics(** Generalisation of {! Proofview.tclINDEPENDENT} *)lettypedIndependant(tactic:M.elttactic):M.elttactic=tclINDEPENDENTLtactic>>=funelts->tclUNIT@@List.fold_leftM.mergeM.emptyeltsendmoduleTraceTactics=TypedTactics(structtypeelt=traceletempty:elt=no_traceletmerge:elt->elt->elt=merge_tracesend)(**
Rewrite of [Auto.tclLOG]
Updates the trace contained in the given tactic.
Fails if the hint's name is forbidden, or if the proof will be complete without using all must-use lemmas.
Arguments:
- [pp: Environ.env -> Evd.evar_map -> Pp.t * Pp.t]: function to obtain the printable version of [(hint_name, source_hint_database)]
- [tac: trace tactic]: tactic that will be tried
- [must_use: : Pp.t list]: list of tactics that must be used during the automation
- [forbidden: : Pp.t list]: list of tactics that mustn't be used during the automation
*)lettclLOG(pp:Environ.env->Evd.evar_map->Pp.t*Pp.t)(tac:tracetactic)(forbidden:Pp.tlist):tracetactic=(tclIFCATCH(tac>>=funtrace->tclENV>>=funenv->tclEVARMAP>>=funsigma->let(hint,src)=ppenvsigmainifList.memhintforbiddenthentclZERO~info:(Exninfo.reify())(SearchBoundtrace)elsetclUNIT{tracewithtrace=(true,trace.current_depth,hint,src)::trace.trace})tclUNIT(fun(exn,info)->tclENV>>=funenv->tclEVARMAP>>=funsigma->let(hint,src)=ppenvsigmainlettrace=beginmatchexnwith|SearchBoundtrace->trace|_->no_traceendintclZERO~info(SearchBound(merge_tracestrace@@singleton_tracefalsehintsrc))))(**
Checks if every hint in [must_use] is contained in [tac] and returns an exception if not
*)lettrace_check_used(must_use:tlist)(trace:trace):tracetactic=letused_lemmas=refStringMap.emptyinList.iter(funname->used_lemmas:=StringMap.add(string_of_ppcmdsname)false!used_lemmas)must_use;List.iter(fun(_,_,hint,_)->used_lemmas:=StringMap.update(string_of_ppcmdshint)(funvalue->matchvaluewith|None->None|Some_->Sometrue)!used_lemmas)trace.trace;letunused_lemmas=ref[]inifStringMap.exists(funnameis_used->if(notis_used)thenunused_lemmas:=name::!unused_lemmas;notis_used)!used_lemmasthentclZERO~info:(Exninfo.reify())(SearchBound(failedtrace))elsetclUNITtrace(**
Rewrite of {! Tacticals.tclORELSE0} to give the trace of the failed tactic instead of the exception
*)lettclOrElse0(tac:tracetactic)(f:trace->tracetactic):tracetactic=TraceTactics.typedIndependant@@tclORELSEtacbeginfun(e,info)->matchewith|SearchBoundtrace->ftrace|_->fno_traceend(**
Wrapper around {! tclOrElse0} with merge of trace contained in the failed [trace tactic] into the second one
*)lettclTraceOrElse(tac1:tracetactic)(tac2:tracetactic):tracetactic=tclOrElse0tac1@@funtrace1->tac2>>=funtrace2->tclUNIT@@merge_traces(failedtrace1)trace2(**
Rewrite of {! Tacticals.tclTraceFirst} with [trace tactic] with a merge of traces of failed tactics
*)lettclTraceFirst(tacs:tracetacticlist):tracetactic=letrecaux(tacs:tracetacticlist)(failed_trace:trace):tracetactic=matchtacswith|[]->letinfo=Exninfo.reify()intclZERO~info(SearchBoundfailed_trace)|tac::rest->tclOrElse0tac@@funtrace1->auxrest{failed_tracewithtrace=List.append(failedtrace1).tracefailed_trace.trace}inauxtacsno_trace(**
Rewrite of Coq's hint printer to keep only the necessary parts
*)letpr_hint(env:Environ.env)(sigma:Evd.evar_map)(h:FullHint.t)=letpr_hint_eltenvsigmah=Printer.pr_econstr_envenvsigma(snd@@hint_as_termh)inmatchFullHint.reprhwith|Res_pfc->pr_hint_eltenvsigmac|ERes_pfc->pr_hint_eltenvsigmac|Give_exactc->pr_hint_eltenvsigmac|Res_pf_THEN_trivial_failc->pr_hint_eltenvsigmac|Unfold_nthc->Printer.pr_evaluable_referencec|Extern(_,tac)->Pputils.pr_glb_genericenvsigmatac