123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200(************************************************************************)(* * 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) *)(************************************************************************)openProoftypet=|Dashofint|Starofint|Plusofintletbullet_eqb1b2=matchb1,b2with|Dashn1,Dashn2->n1=n2|Starn1,Starn2->n1=n2|Plusn1,Plusn2->n1=n2|_->falseletpr_bulletb=matchbwith|Dashn->Pp.(str(String.maken'-'))|Starn->Pp.(str(String.maken'*'))|Plusn->Pp.(str(String.maken'+'))typebehavior={name:string;put:Proof.t->t->Proof.t;suggest:Proof.t->Pp.t}letbehaviors=Hashtbl.create4letregister_behaviorb=Hashtbl.addbehaviorsb.nameb(*** initial modes ***)letnone={name="None";put=(funx_->x);suggest=(fun_->Pp.mt())}let_=register_behaviornonemoduleStrict=structtypesuggestion=|Suggestoft(* this bullet is mandatory here *)|Unfinishedoft(* no mandatory bullet here, but this bullet is unfinished *)|NoBulletInUse(* No mandatory bullet (or brace) here, no bullet pending,
some focused goals exists. *)|NeedClosingBrace(* Some unfocussed goal exists "{" needed to focus them *)|ProofFinished(* No more goal anywhere *)(* give a message only if more informative than the standard coq message *)letsuggest_on_solved_goalsugg=matchsuggwith|NeedClosingBrace->Pp.(str"Try unfocusing with \"}\".")|NoBulletInUse->Pp.mt()|ProofFinished->Pp.mt()|Suggestb->Pp.(str"Focus next goal with bullet "++pr_bulletb++str".")|Unfinishedb->Pp.(str"The current bullet "++pr_bulletb++str" is unfinished.")(* give always a message. *)letsuggest_on_errorsugg=matchsuggwith|NeedClosingBrace->Pp.(str"Try unfocusing with \"}\".")|NoBulletInUse->assertfalse(* This should never raise an error. *)|ProofFinished->Pp.(str"No more goals.")|Suggestb->Pp.(str"Expecting "++pr_bulletb++str".")|Unfinishedb->Pp.(str"Current bullet "++pr_bulletb++str" is not finished.")exceptionFailedBulletoft*suggestionlet_=CErrors.register_handler(function|FailedBullet(b,sugg)->letprefix=Pp.(str"Wrong bullet "++pr_bulletb++str": ")inSomePp.(str"[Focus]"++spc()++prefix++suggest_on_errorsugg)|_->None)(* spiwack: we need only one focus kind as we keep a stack of (distinct!) bullets *)letbullet_kind=(new_focus_kind():tlistfocus_kind)letbullet_cond=done_cond~loose_end:truebullet_kind(* spiwack: as it is bullets are reset (locally) by *any* non-bullet focusing command
experience will tell if this is the right discipline of if we want to be finer and
reset them only for a choice of bullets. *)letget_bulletspr=ifis_last_focusbullet_kindprthenget_at_focusbullet_kindprelse[]lethas_bulletbulpr=letrechas_bullet=function|b'::_whenbullet_eqbulb'->true|_::l->has_bulletl|[]->falseinhas_bullet(get_bulletspr)(* pop a bullet from proof [pr]. There should be at least one
bullet in use. If pop impossible (pending proofs on this level
of bullet or higher) then raise [Proof.CannotUnfocusThisWay]. *)letpoppr=matchget_bulletsprwith|b::_->unfocusbullet_kindpr(),b|_->assertfalseletpush(b:t)pr=focusbullet_cond(b::get_bulletspr)1prletsuggest_bullet(prf:Proof.t):suggestion=ifis_doneprfthenProofFinishedelseifnot(no_focused_goalprf)then(* No suggestion if a bullet is not mandatory, look for an unfinished bullet *)matchget_bulletsprfwith|b::_->Unfinishedb|_->NoBulletInUseelse(* There is no goal under focus but some are unfocussed,
let us look at the bullet needed. *)letrecloopprf=matchpopprfwith|prf,b->(* pop went well, this means that there are no more goals
*under this* bullet b, see if a new b can be pushed. *)begintryignore(pushbprf);Suggestbwith_->(* b could not be pushed, so we must look for a outer bullet *)loopprfend|exception_->(* No pop was possible, but there are still
subgoals somewhere: there must be a "}" to use. *)NeedClosingBraceinloopprfletrecpop_until(prf:Proof.t)bul:Proof.t=letprf',b=popprfinifbullet_eqbulbthenprf'elsepop_untilprf'bulletputpbul=tryifnot(has_bulletbulp)then(* bullet is not in use, so pushing it is always ok unless
no goal under focus. *)pushbulpelsematchsuggest_bulletpwith|Suggestsuggested_bulletwhenbullet_eqbulsuggested_bullet->(* suggested_bullet is mandatory and you gave the right one *)letp'=pop_untilpbulinpushbulp'(* the bullet you gave is in use but not the right one *)|sugg->raise(FailedBullet(bul,sugg))withNoSuchGoals_->(* push went bad *)raise(FailedBullet(bul,suggest_bulletp))letstrict={name="Strict Subproofs";put=put;suggest=(funprf->suggest_on_solved_goal(suggest_bulletprf))}let_=register_behaviorstrictend(* Current bullet behavior, controlled by the option *)let{Goptions.get=current_behavior}=Goptions.declare_interpreted_string_option_and_ref~key:["Bullet";"Behavior"]~value:Strict.strict(funn->tryHashtbl.findbehaviorsnwithNot_found->CErrors.user_errPp.(str("Unknown bullet behavior: \""^n^"\".")))(funv->v.name)()letputpb=(current_behavior()).putpbletsuggestp=(current_behavior()).suggestplet_=CErrors.register_handlerbeginfunction|Proof.SuggestNoSuchGoals(n,proof)->letsuffix=suggestproofinSome(Pp.(str"No such "++str(CString.pluraln"goal")++str"."++pr_non_empty_arg(funx->x)suffix))|_->Noneend