123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780(************************************************************************)(* * 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) *)(************************************************************************)moduleStdList=ListopenUtilopenNamesopenPpopenTacexprlet(ltac_trace_info:ltac_stackExninfo.t)=Exninfo.make"ltac_trace"letprtacx=letenv=Global.env()inPptactic.pr_glob_tacticenvx(* This module intends to be a beginning of debugger for tactic expressions.
Currently, it is quite simple and we can hope to have, in the future, a more
complete panel of commands dedicated to a proof assistant framework *)(* Debug information *)typedebug_info=|DebugOnofint|DebugOff(* An exception handler *)letexplain_logic_errore=CErrors.printeletexplain_logic_error_no_anomalye=CErrors.print_no_reportetypebreakpoint={dirpath:string;(* module dirpath *)offset:int;}moduleBPSet=CSet.Make(structtypet=breakpointletcompareb1b2=letc1=Int.compareb1.offsetb2.offsetinifc1<>0thenc1elseString.compareb1.dirpathb2.dirpathend)letbreakpoints=refBPSet.empty(** add or remove a single breakpoint. Maps the breakpoint from
IDE format (absolute path name, offset) to (module dirpath, offset)
opt - true to add, false to remove
ide_bpt - the breakpoint (absolute path name, offset)
*)letupdate_bptfnameoffsetopt=letopenNamesinletdp=iffname="ToplevelInput"then(* todo: or None? *)DirPath.make[Id.of_string"Top"]elsebegin(* find the DirPath matching the absolute pathname of the file *)(* ? check for .v extension? *)letdirname=Filename.dirnamefnameinletbasename=Filename.basenamefnameinletbase_id=Id.of_string(Filename.remove_extensionbasename)inDirPath.make(base_id::(tryletp=Loadpath.find_load_path(CUnix.physical_path_of_stringdirname)inDirPath.repr(Loadpath.logicalp)with_->[]))endinletdirpath=DirPath.to_stringdpinletbp={dirpath;offset}in(* Printf.printf "update_bpt: %s -> %s %d\n%!" fname dirpath ide_bpt.offset;*)matchoptwith|true->breakpoints:=BPSet.addbp!breakpoints|false->breakpoints:=BPSet.removebp!breakpointsletupd_bptsupdates=List.iter(funop->let((file,offset),opt)=opin(* Printf.printf "Coq upd_bpts %s %d %b\n%!" file offset opt;*)update_bptfileoffsetopt;)updatestypedebugger_state={(* location of next code to execute, is not in stack *)mutablecur_loc:Loc.toption;(* yields the call stack *)mutablestack:(string*Loc.toption)list;(* variable value maps for each stack frame *)mutablevarmaps:Geninterp.Val.tNames.Id.Map.tlist;}letdebugger_state={cur_loc=None;stack=[];varmaps=[]}letget_stack()=(* Printf.printf "server: db_stack call\n%!";*)letrecshiftsprev_locres=matchswith|(tacn,loc)::tl->shifttlloc(((Sometacn),prev_loc)::res)|[]->(None,prev_loc)::resinList.rev(shiftdebugger_state.stackdebugger_state.cur_loc[])moduleCSet=CSet.Make(Names.DirPath)letbad_dirpaths=refCSet.empty(* cvt_loc, format_frame and format_stack belong elsewhere *)letcvt_locloc=letopenLocinmatchlocwith|Some{fname=ToplevelInput;bp;ep}->Some("ToplevelInput",[bp;ep])|Some{fname=InFile{dirpath=None;file};bp;ep}->Some(file,[bp;ep])(* for Load command *)|Some{fname=InFile{dirpath=(Somedirpath)};bp;ep}->letopenNamesinletdirpath=DirPath.make(List.rev_map(funi->Id.of_stringi)(String.split_on_char'.'dirpath))inletpfx=DirPath.make(List.tl(DirPath.reprdirpath))inletpaths=Loadpath.find_with_logical_pathpfxinletbasename=matchDirPath.reprdirpathwith|hd::tl->(Id.to_stringhd)^".v"|[]->""inletvs_files=List.map(funp->(Filename.concat(Loadpath.physicalp)basename))pathsinletfiltered=List.filter(funp->Sys.file_existsp)vs_filesinbeginmatchfilteredwith|[]->(* todo: maybe tweak this later to allow showing a popup dialog in the GUI *)ifnot(CSet.memdirpath!bad_dirpaths)thenbeginbad_dirpaths:=CSet.adddirpath!bad_dirpaths;letmsg=Pp.(fnl()++str"Unable to locate source code for module "++str(Names.DirPath.to_stringdirpath))inletmsg=ifvs_files=[]thenmsgelse(List.fold_left(funmsgf->msg++fnl()++strf)(msg++str" in:")vs_files)inFeedback.msg_warningmsgend;None|[f]->Some(f,[bp;ep])|f::tl->ifnot(CSet.memdirpath!bad_dirpaths)thenbeginbad_dirpaths:=CSet.adddirpath!bad_dirpaths;letmsg=Pp.(fnl()++str"Multiple files found matching module "++str(Names.DirPath.to_stringdirpath)++str":")inletmsg=List.fold_left(funmsgf->msg++fnl()++strf)msgvs_filesinFeedback.msg_warningmsgend;Some(f,[bp;ep])(* be arbitrary unless we can tell which file was loaded *)end|None->None(* nothing to highlight, e.g. not in a .v file *)letformat_frametextloc=tryletopenLocinmatchlocwith|Some{fname=InFile{dirpath=(Somedp)};line_nb}->letdplen=String.lengthdpinletlastdot=String.rindexdp'.'inletfile=String.subdp(lastdot+1)(dplen-(lastdot+1))inletmodule_name=String.subdp0lastdotinletroutine=try(* try text as a kername *)assert(CString.is_prefixdptext);letknlen=String.lengthtextinletlastdot=String.rindextext'.'inString.subtext(lastdot+1)(knlen-(lastdot+1))with_->textinPrintf.sprintf"%s:%d, %s (%s)"routineline_nbfilemodule_name;|Some{fname=ToplevelInput;line_nb}->letitems=String.split_on_char'.'textinPrintf.sprintf"%s:%d, %s"(List.nthitems1)line_nb(List.hditems);|_->textwith_->textletformat_stacks=List.map(fun(tac,loc)->letfloc=cvt_loclocinmatchtacwith|Sometacn->lettacn=ifloc=Nonethentacn^" (no location)"elseformat_frametacnlocin(tacn,floc)|None->matchlocwith|Some{Loc.line_nb}->(":"^(string_of_intline_nb),floc)|None->(": (no location)",floc))sletget_varsframenum=letopenNamesin(* Printf.printf "server: db_vars call\n%!";*)letvars=List.nthdebugger_state.varmapsframenuminList.map(funb->let(id,v)=bin(Id.to_stringid,Pptactic.pr_valueConstrexpr.LevelSomev))(Id.Map.bindingsvars)[@@@ocaml.warning"-32"]letcmd_to_strcmd=letopenDebugHook.Actioninmatchcmdwith|Continue->"Continue"|StepIn->"StepIn"|StepOver->"StepOver"|StepOut->"StepOut"|Skip->"Skip"|Interrupt->"Interrput"|Help->"Help"|UpdBpts_->"UpdBpts"|Configd->"Configd"|GetStack->"GetStack"|GetVars_->"GetVars"|RunCnt_->"RunCnt"|RunBreakpoint_->"RunBreakpoint"|Command_->"Command"|Failed->"Failed"|Ignore->"Ignore"[@@@ocaml.warning"+32"]letaction=refDebugHook.Action.StepOverletbreak=reffalse(* causes the debugger to stop at the next step *)letget_break()=!breakletset_breakb=break:=b(* Communications with the outside world *)moduleComm=structlethook()=Option.get(DebugHook.Intf.get())letwrap=Proofview.NonLogical.make(* TODO: ideally we would check that the debugger hooks are
correctly set, however we don't do this yet as debugger
initialization is unconditionally done for example in coqc.
Improving this would require some tweaks in tacinterp which
are out of scope for the current refactoring. *)letinit()=letopenDebugHookinmatchIntf.get()with|Someintf->ifIntf.(intf.isTerminal)thenaction:=Action.StepInelsebeginset_breakfalse;breakpoints:=BPSet.empty;(hook()).Intf.submit_answer(Answer.Init);whileletcmd=(hook()).Intf.read_cmd()inletopenDebugHook.Actioninmatchcmdwith|UpdBptsupdates->upd_bptsupdates;true|Configd->action:=Action.Continue;false|_->failwith"Action type not allowed"do()doneend|None->()(* CErrors.user_err
* (Pp.str "Your user interface does not support the Ltac debugger.") *)openDebugHook.IntfopenDebugHook.Answerletpromptg=wrap(fun()->(hook()).submit_answer(Promptg))letgoalg=wrap(fun()->(hook()).submit_answer(Goalg))letoutputg=wrap(fun()->(hook()).submit_answer(Outputg))(* routines for deferring output; output is sent only if
the debugger stops at the next step *)letout_queue=Queue.create()letdefer_outputf=wrap(fun()->Queue.addfout_queue)letprint_deferred()=wrap(fun()->whilenot(Queue.is_emptyout_queue)do(hook()).submit_answer(Output((Queue.popout_queue)()))done)letclear_queue()=wrap(fun()->Queue.clearout_queue)[@@@ocaml.warning"-32"]letprintg=(hook()).submit_answer(Output(strg))[@@@ocaml.warning"+32"]letisTerminal()=(hook()).isTerminalletread=wrap(fun()->letrecl()=letcmd=(hook()).read_cmd()inletopenDebugHook.Actioninmatchcmdwith|Ignore->l()|UpdBptsupdates->upd_bptsupdates;l()|GetStack->((hook)()).submit_answer(Stack(format_stack(get_stack())));l()|GetVarsframenum->((hook)()).submit_answer(Vars(get_varsframenum));l()|_->action:=cmd;cmdinl())endletdefer_output=Comm.defer_output(* Prints the goal *)letdb_pr_goalgl=letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletconcl=Proofview.Goal.conclglinletpenv=Termops.Internal.print_named_contextenvsigmainletpc=Printer.pr_econstr_envenvsigmaconclinstr" "++hv0(penv++fnl()++str"============================"++fnl()++str" "++pc)++fnl()++fnl()letdb_pr_goal=letopenProofviewinletopenNotationsinGoal.goals>>=fungl->Monad.List.map(funx->x)gl>>=fungls->letpg=str(CString.plural(List.lengthgls)"Goal")++str":"++fnl()++Pp.seq(List.mapdb_pr_goalgls)inProofview.tclLIFT(Comm.goalpg)(* Prints the commands *)lethelp()=Comm.output(str"Commands: <Enter> = Step"++fnl()++str" h/? = Help"++fnl()++str" r <num> = Run <num> times"++fnl()++str" r <string> = Run up to next idtac <string>"++fnl()++str" s = Skip"++fnl()++str" x = Exit")[@@@ocaml.warning"-32"]lettac_loctac=letopenTacexprinletopenCAstinletloc,tac=tac.loc,tac.vinletrv=matchtacwith|TacAtom_->"TacAtom"|TacThen_->"TacThen"|TacDispatch_->"TacDispatch"|TacExtendTac_->"TacExtendTac"|TacThens_->"TacThens"|TacThens3parts_->"TacThens3parts"|TacFirst_->"TacFirst"|TacSolve_->"TacSolve"|TacTry_->"TacTry"|TacOr_->"TacOr"|TacOnce_->"TacOnce"|TacExactlyOnce_->"TacExactlyOnce"|TacIfThenCatch_->"TacIfThenCatch"|TacOrelse_->"TacOrelse"|TacDo_->"TacDo"|TacTimeout_->"TacTimeout"|TacTime_->"TacTime"|TacRepeat_->"TacRepeat"|TacProgress_->"TacProgress"|TacAbstract_->"TacAbstract"|TacId_->"TacId"|TacFail_->"TacFail"|TacLetIn_->"TacLetIn"|TacMatch_->"TacMatch"|TacMatchGoal_->"TacMatchGoal"|TacFun_->"TacFun"|TacArg_->"TacArg"|TacSelect_->"TacSelect"|TacML_->"TacML"|TacAlias_->"TacAlias"in(* Printf.printf " %s\n%!" (fst rv);*)rv,locletprint_locdescloc=letopenLocinmatchlocwith|Someloc->letsrc=(matchloc.fnamewith|InFile{file}->file|ToplevelInput->"ToplevelInput")inPrintf.sprintf"%s: %s %d/%d %d:%d\n"descsrcloc.bploc.line_nb(loc.bp-loc.bol_pos_last)(loc.ep-loc.bol_pos_last)|None->Printf.sprintf"%s: loc is None"descletprint_loc_tactac=let(desc,loc)=tac_loctacinprint_locdescloc[@@@ocaml.warning"+32"]letcvt_stackstack=List.map(funk->let(loc,k)=kin(* todo: compare to explain_ltac_call_trace below *)matchkwith|LtacNameCalll->KerName.to_stringl,loc|LtacMLCall_->"??? LtacMLCall",loc(* LtacMLCall should not even show the stack frame, but profiling may need it *)|LtacNotationCalll->"??? LtacNotationCall",loc(* LtacNotationCall should not even show the stack frame, but profiling may need it *)|LtacAtomCall_->"??? LtacAtomCall",loc(* not found in stack *)|LtacVarCall(kn,id,e)->letfn_name=matchknwith|Somekn->KerName.to_stringkn|None->""(* anonymous function *)infn_name,loc|LtacConstrInterp_->"",loc)stack(* Each list entry contains multiple trace frames. *)lettrace_chunks:ltac_tracelistref=ref[([],[])]letpush_chunktrace=trace_chunks:=trace::!trace_chunksletpop_chunktrace=trace_chunks:=List.tl!trace_chunksletprev_stack=ref(Some[])(* previous stopping point in debugger *)letprev_trace_chunks:ltac_tracelistref=ref[([],[])]letsave_loctacvarmaptrace=(* Comm.print (print_loc_tac tac);*)letstack,varmaps=matchtracewith|Some(stack,varmaps)->stack,varmaps|None->[],[]indebugger_state.cur_loc<-CAst.(tac.loc);let(pstack,pvars)=List.fold_right(fun(s,v)(os,ov)->(s@os),(v@ov))!trace_chunks([],[])indebugger_state.stack<-cvt_stack(stack@pstack);debugger_state.varmaps<-varmap::(varmaps@pvars)(* Prints the goal and the command to be executed *)letgoal_comtacvarmaptrace=save_loctacvarmaptrace;Proofview.tclTHENdb_pr_goal(ifComm.isTerminal()||debugger_state.cur_loc=Nonethen(Proofview.tclLIFT(Comm.output(str"Going to execute:"++fnl()++prtactac)))elseProofview.tclLIFT(Proofview.NonLogical.return()))(* [run (new_ref _)] gives us a ref shared among [NonLogical.t]
expressions. It avoids parameterizing everything over a
reference. *)letskipped=Proofview.NonLogical.run(Proofview.NonLogical.ref0)letskip=Proofview.NonLogical.run(Proofview.NonLogical.ref0)letidtac_breakpt=Proofview.NonLogical.run(Proofview.NonLogical.refNone)letidtac_bpt_stop=reffalseletbatch=reffalseopenGoptionslet()=declare_bool_option{optstage=Summary.Stage.Interp;optdepr=None;optkey=["Ltac";"Batch";"Debug"];optread=(fun()->!batch);optwrite=(funx->batch:=x)}(* (Re-)initialize debugger. is_tac controls whether to set the action *)letdb_initializeis_tac=ifSys.os_type="Unix"thenSys.set_signalSys.sigusr1(Sys.Signal_handle(fun_->set_breaktrue));letopenProofview.NonLogicalinletx=(skip:=0)>>(skipped:=0)>>(idtac_breakpt:=None)inifis_tacthenbeginidtac_bpt_stop.contents<-false;makeComm.init>>xendelsex(* Prints the run counter *)letprint_run_ctrprint=letopenProofview.NonLogicalinifprintthenbegin!skipped>>=funskipped->Comm.output(str"Executed expressions: "++intskipped++fnl())end>>!skipped>>=funx->skipped:=x+1elsereturn()(* Prints the prompt *)letrecpromptlevel=letrunnoprint=print_run_ctrfalseinletopenProofview.NonLogicalinletnl=ifStdlib.(!batch)then"\n"else""inComm.print_deferred()>>Comm.prompt(tag"message.prompt"@@fnl()++str"TcDebug ("++intlevel++str(") > "^nl))>>ifStdlib.(!batch)&&Comm.isTerminal()thenreturn(DebugOn(level+1))elseletexit=(skip:=0)>>(skipped:=0)>>raise(Sys.Break,Exninfo.null)inComm.read>>=funinst->letopenDebugHook.Actioninmatchinstwith|Continue|StepIn|StepOver|StepOut->return(DebugOn(level+1))|Skip->returnDebugOff|Interrupt->Proofview.NonLogical.print_char'\b'>>exit(* todo: why the \b? *)|Help->help()>>promptlevel|UpdBptsupdates->failwith"UpdBpts"(* handled in init() loop *)|Configd->failwith"Configd"(* handled in init() loop *)|GetStack->failwith"GetStack"(* handled in read() loop *)|GetVars_->failwith"GetVars"(* handled in read() loop *)|RunCntnum->(skip:=num)>>(skipped:=0)>>runnoprint>>return(DebugOn(level+1))|RunBreakpoints->(idtac_breakpt:=(Somes))>>(* todo: look in Continue? *)runnoprint>>return(DebugOn(level+1))|Command_->failwith"Command"(* not possible *)|Failed->promptlevel|Ignore->failwith"Ignore"(* not possible *)letat_breakpointtac=letopenLocinletcheck_bptdirpathoffset=(* Printf.printf "In tactic_debug, dirpath = %s offset = %d\n%!" dirpath offset;*)BPSet.mem{dirpath;offset}!breakpointsinmatchCAst.(tac.loc)with|Some{fname=InFile{dirpath=Somedirpath};bp}->check_bptdirpathbp|Some{fname=ToplevelInput;bp}->check_bpt"Top"bp|_->false[@@@ocaml.warning"-32"]openTacexprletpr_call_kindnk=let(loc,k)=kinletkind=(matchkwith|LtacMLCall_->"LtacMLCall"|LtacNotationCall_->"LtacNotationCall"|LtacNameCalll->letname=(KerName.to_stringl)^(print_loc""loc)inPrintf.printf"%s\n%!"name;Feedback.msg_notice(Pp.strname);"LtacNameCall"|LtacAtomCall_->"LtacAtomCall"|LtacVarCall_->"LtacVarCall"|LtacConstrInterp_->"LtacConstrInterp")inPrintf.printf"stack %d: %s\n%!"nkindletdump_stackmsgstack=matchstackwith|Somestack->Printf.printf"%s: stack len = %d\n"msg(StdList.lengthstack);StdList.iteripr_call_kindstack;|None->()letdump_varmapsmsgvarmaps=matchvarmapswith|Somevarmaps->List.iter(funvarmap->Printf.printf"%s: varmap len = %d\n"msg(Id.Map.cardinalvarmap);List.iter(funb->let(k,b)=binPrintf.printf"id = %s\n%!"(Id.to_stringk);ignore@@Pptactic.pr_valueConstrexpr.LevelSomeb(* todo: LevelSome?? *)(* b is Geninterp.Val.t Names.Id.Map.t *))(Id.Map.bindingsvarmap))varmaps|None->()[@@@ocaml.warning"+32"](* Prints the state and waits for an instruction *)(* spiwack: the only reason why we need to take the continuation [f]
as an argument rather than returning the new level directly seems to
be that [f] is wrapped in with "explain_logic_error". I don't think
it serves any purpose in the current design, so we could just drop
that. *)letdebug_promptlevtacfvarmaptrace=(* trace omits the currently-running tactic, so add separately *)letstack,varmaps=matchtracewith|Some(stack,varmaps)->Somestack,Some(varmap::varmaps)|None->None,Some[varmap]inletrunprint=print_run_ctrtrueinletopenProofview.NonLogicalinlet(>=)=Proofview.tclBINDin(* What to print and to do next *)letnewlevel=Proofview.tclLIFT!skip>=funs->letstop_here()=(* dump_stack "at debug_prompt" stack;*)(* dump_varmaps "at debug_prompt" varmaps;*)prev_stack.contents<-stack;prev_trace_chunks.contents<-trace_chunks.contents;Proofview.tclTHEN(goal_comtacvarmaptrace)(Proofview.tclLIFT(promptlev))inletstacks_infostackp_stack=(* performance impact? *)letst_chunks=StdList.map(fun(s,_)->s)trace_chunks.contentsinletst=StdList.concat((Option.default[]stack)::st_chunks)inletprev_st_chunks=StdList.map(fun(s,_)->s)prev_trace_chunks.contentsinletst_prev=StdList.concat((Option.default[]p_stack)::prev_st_chunks)inletl_cur,l_prev=StdList.lengthst,StdList.lengthst_previnst,st_prev,l_cur,l_previnletp_stack=prev_stack.contentsinifaction.contents=DebugHook.Action.Continue&&at_breakpointtacthen(* todo: skip := 0 *)stop_here()elseifget_break()thenbeginset_breakfalse;stop_here()endelseifs=1thenbeginProofview.tclLIFT((skip:=0)>>runprint)>=(fun()->stop_here())endelseifs>0thenProofview.tclLIFT((skip:=s-1)>>runprint>>!skip>>=funnew_skip->(ifnew_skip=0thenskipped:=0elsereturn())>>return(DebugOn(lev+1)))elseifidtac_bpt_stop.contentsthenbeginidtac_bpt_stop.contents<-false;stop_here()endelseProofview.tclLIFT!idtac_breakpt>=funidtac_breakpt->ifOption.has_someidtac_breakptthenProofview.tclLIFT(runprint>>return(DebugOn(lev+1)))elsebeginletopenDebugHook.Actioninletstop=matchaction.contentswith|Continue->false|StepIn->true|StepOver->letst,st_prev,l_cur,l_prev=stacks_infostackp_stackinifl_cur=0||l_cur<l_prevthentrue(* stepped out *)elseifl_prev=0(*&& l_cur > 0*)thenfalseelseletpeq=StdList.nthst(l_cur-l_prev)==(StdList.hdst_prev)in(l_cur>l_prev&&(notpeq))||(* stepped out *)(l_cur=l_prev&&peq)(* stepped over *)|StepOut->letst,st_prev,l_cur,l_prev=stacks_infostackp_stackinifl_cur<l_prevthentrueelseifl_prev=0thenfalseelseStdList.nthst(l_cur-l_prev)!=(StdList.hdst_prev)|Skip|RunCnt_|RunBreakpoint_->false(* handled elsewhere *)|_->failwith"action op"inifstopthenbeginstop_here()endelseProofview.tclLIFT(Comm.clear_queue()>>return(DebugOn(lev+1)))endinnewlevel>=funnewlevel->(* What to execute *)Proofview.tclOR(fnewlevel)beginfun(reraise,info)->Proofview.tclTHEN(Proofview.tclLIFTbegin(skip:=0)>>(skipped:=0)>>Comm.defer_output(fun()->str"Level "++intlev++str": "++explain_logic_errorreraise)end)(Proofview.tclZERO~inforeraise)endletis_debugdb=letopenProofview.NonLogicalin!idtac_breakpt>>=funidtac_breakpt->matchdb,idtac_breakptwith|DebugOff,_->returnfalse|_,Some_->returnfalse|_->!skip>>=funskip->return(skip=0)(* Prints a constr *)letdb_constrdebugenvsigmac=letopenProofview.NonLogicalinis_debugdebug>>=fundb->ifdbthenComm.defer_output(fun()->str"Evaluated term: "++Printer.pr_econstr_envenvsigmac)elsereturn()letis_breakpointbrknames=matchbrkname,swith|Somes,MsgStrings'::_->String.equalss'|_->falseletdb_breakpointdebugs=letopenProofview.NonLogicalin!idtac_breakpt>>=funopt_breakpoint->matchdebugwith|DebugOnlevwhennot(CList.is_emptys)&&is_breakpointopt_breakpoints->idtac_bpt_stop.contents<-true;idtac_breakpt:=None|_->return()(** Extracting traces *)letis_defined_ltactrace=letrecaux=function|(_,Tacexpr.LtacNameCallf)::_->not(Tacenv.is_ltac_for_ml_tacticf)|(_,Tacexpr.LtacNotationCallf)::_->true|(_,Tacexpr.LtacAtomCall_)::_->false|_::tail->auxtail|[]->falseinaux(List.revtrace)letexplain_ltac_call_tracelasttrace=letcalls=last::List.rev_mapsndtraceinletpr_callck=matchckwith|Tacexpr.LtacNotationCallkn->quote(Pptactic.pr_alias_keykn)|Tacexpr.LtacNameCallcst->quote(Pptactic.pr_ltac_constantcst)|Tacexpr.LtacMLCallt->quote(prtact)|Tacexpr.LtacVarCall(_,id,t)->quote(Id.printid)++strbrk" (bound to "++prtact++str")"|Tacexpr.LtacAtomCallte->quote(prtac(CAst.make(Tacexpr.TacAtomte)))|Tacexpr.LtacConstrInterp(env,sigma,c,{Ltac_pretype.ltac_constrs=vars})->quote(Printer.pr_glob_constr_envenvsigmac)++(ifnot(Id.Map.is_emptyvars)thenstrbrk" (with "++prlist_with_seppr_comma(fun(id,c)->Id.printid++str":="++Printer.pr_lconstr_under_binders_envenvsigmac)(List.rev(Id.Map.bindingsvars))++str")"elsemt())inmatchcallswith|[]->mt()|[a]->hov0(str"Ltac call to "++pr_calla++str" failed.")|_->letkind_of_last_call=matchList.lastcallswith|Tacexpr.LtacConstrInterp_->", last term evaluation failed."|_->", last call failed."inhov0(str"In nested Ltac calls to "++pr_enumpr_callcalls++strbrkkind_of_last_call)letskip_extensionstrace=letrecaux=function|(_,Tacexpr.LtacNotationCall_astac)::(_,Tacexpr.LtacMLCall_)::tail->(* Case of an ML defined tactic with entry of the form <<"foo" args>> *)(* see tacextend.mlp *)tac::auxtail|t::tail->t::auxtail|[]->[]inList.rev(aux(List.revtrace))letextract_ltac_tracetrace=lettrace=skip_extensionstraceinlet(_,c),tail=List.sep_lasttraceinifis_defined_ltactracethen(* We entered a user-defined tactic,
we display the trace with location of the call *)letmsg=hov0(explain_ltac_call_tracectail++fnl())inmsgelsemt()letget_ltac_traceinfo=letltac_trace=Exninfo.getinfoltac_trace_infoinmatchltac_tracewith|None->None|Sometrace->Some(extract_ltac_tracetrace)let()=CErrors.register_additional_error_infoget_ltac_trace