123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214(************************************************************************)(* * 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) *)(************************************************************************)(* Parsing of vernacular. *)openPpopenCErrorsopenUtilopenVernacexpropenVernacprop(* The functions in this module may raise (unexplainable!) exceptions.
Use the module Coqtoplevel, which catches these exceptions
(the exceptions are explained only at the toplevel). *)letchecknav{CAst.loc;v={expr}}=ifis_navigation_vernacexpr&¬(is_resetexpr)thenCErrors.user_err?loc(str"Navigation commands forbidden in files.")(* Echo from a buffer based on position.
XXX: Should move to utility file. *)letvernac_echo?locin_chan=letopenLocinOption.iter(funloc->letlen=loc.ep-loc.bpinseek_inin_chanloc.bp;Feedback.msg_notice@@str@@really_input_stringin_chanlen)loc(* Re-enable when we get back to feedback printing *)(* let is_end_of_input any = match any with *)(* Stm.End_of_input -> true *)(* | _ -> false *)typetime_output=|ToFeedback|ToChannelofFormat.formatterletmake_time_output=function|Coqargs.ToFeedback->ToFeedback|ToFilef->ToChannel(Format.formatter_of_out_channel(open_outf))moduleState=structtypet={doc:Stm.doc;sid:Stateid.t;proof:Proof.toption;time:time_outputoption;}endletemit_timestatecomtstarttend=matchstate.State.timewith|None->()|Sometime->letpp=Topfmt.pr_cmd_headercom++System.fmt_time_differencetstarttendinmatchtimewith|ToFeedback->Feedback.msg_noticepp|ToChannelch->Pp.pp_withch(pp++fnl())letinterp_vernac~check~state({CAst.loc;_}ascom)=letopenStateintryletdoc,nsid,ntip=Stm.add~doc:state.doc~ontop:state.sid(not!Flags.quiet)comin(* Main STM interaction *)ifntip<>Stm.NewAddTipthenanomaly(str"vernac.ml: We got an unfocus operation on the toplevel!");(* Force the command *)let()=ifcheckthenStm.observe~docnsidinletnew_proof=Vernacstate.Declare.give_me_the_proof_opt()[@ocaml.warning"-3"]in{statewithdoc;sid=nsid;proof=new_proof;}withreraise->let(reraise,info)=Exninfo.capturereraiseinletinfo=(* Set the loc to the whole command if no loc *)matchLoc.get_locinfo,locwith|None,Someloc->Loc.add_locinfoloc|Some_,_|_,None->infoinExninfo.iraise(reraise,info)(* Load a vernac file. CErrors are annotated with file and location *)letload_vernac_core~echo~check~state?sourcefile=(* Keep in sync *)letin_chan=open_utf8_file_infileinletin_echo=ifechothenSome(open_utf8_file_infile)elseNoneinletinput_cleanup()=close_inin_chan;Option.iterclose_inin_echoinletsource=Option.default(Loc.InFile{dirpath=None;file})sourceinletin_pa=Pcoq.Parsable.make~loc:Loc.(initialsource)(Gramlib.Stream.of_channelin_chan)inletopenStatein(* ids = For beautify, list of parsed sids *)letrecloopstateids=lettstart=System.get_time()inmatchStm.parse_sentence~doc:state.doc~entry:Pvernac.main_entrystate.sidin_pawith|None->input_cleanup();state,ids,Pcoq.Parsable.commentsin_pa|Someast->(* Printing of AST for -compile-verbose *)Option.iter(vernac_echo?loc:ast.CAst.loc)in_echo;checknavast;letstate=try_finally(fun()->Flags.silently(interp_vernac~check~state)ast)()(fun()->lettend=System.get_time()in(* The -time option is only supported from console-based clients
due to the way it prints. *)emit_timestateasttstarttend)()in(loop[@ocaml.tailcall])state(state.sid::ids)intryloopstate[]withany->(* whatever the exception *)let(e,info)=Exninfo.captureanyininput_cleanup();Exninfo.iraise(e,info)letprocess_expr~stateloc_ast=tryinterp_vernac~check:true~stateloc_astwithreraise->letreraise,info=Exninfo.capturereraisein(* Exceptions don't carry enough state to print themselves (typically missing the nametab)
so we need to print before resetting to an older state. See eg #16745 *)letreraise=UserError(CErrors.iprint(reraise,info))in(* Keep just the loc in the info as it's printed separately *)letinfo=Option.cata(Loc.add_locExninfo.null)Exninfo.null(Loc.get_locinfo)inignore(Stm.edit_at~doc:state.docstate.sid);Exninfo.iraise(reraise,info)letprocess_expr~stateloc_ast=lettstart=System.get_time()intry_finally(fun()->process_expr~stateloc_ast)()(fun()->lettend=System.get_time()inemit_timestateloc_asttstarttend)()(******************************************************************************)(* Beautify-specific code *)(******************************************************************************)(* vernac parses the given stream, executes interpfun on the syntax tree it
* parses, and is verbose on "primitives" commands if verbosely is true *)letbeautify_suffix=".beautified"letset_formatter_translatorch=letoutsbe=output_substringchsbeinletft=Format.make_formatterout(fun()->flushch)inFormat.pp_set_max_boxesftmax_int;ftletpr_new_syntax?locft_beautifyocom=letloc=Option.cataLoc.unloc(0,0)locinletbefore=comment(Pputils.extract_comments(fstloc))inletcom=Option.cataPpvernac.pr_vernac(mt())ocominletafter=comment(Pputils.extract_comments(sndloc))inif!Flags.beautify_filethen(Pp.pp_withft_beautify(hov0(before++com++after));Format.pp_print_flushft_beautify())elseFeedback.msg_info(hov4(str"New Syntax:"++fnl()++(hov0com)))(* load_vernac with beautify *)letbeautify_pass~doc~comments~ids~filename=letft_beautify,close_beautify=if!Flags.beautify_filethenletchan_beautify=open_out(filename^beautify_suffix)inset_formatter_translatorchan_beautify,fun()->close_outchan_beautify;else!Topfmt.std_ft,fun()->()in(* The interface to the comment printer is imperative, so we first
set the comments, then we call print. This has to be done for
each file. *)Pputils.beautify_comments:=comments;List.iter(funid->pr_new_syntaxft_beautify(Stm.get_ast~docid))ids;(* Is this called so comments at EOF are printed? *)pr_new_syntax~loc:(Loc.make_loc(max_int,max_int))ft_beautifyNone;close_beautify()(* Main driver for file loading. For now, we only do one beautify
pass. *)letload_vernac~echo~check~state?sourcefilename=letostate,ids,comments=load_vernac_core~echo~check~state?sourcefilenamein(* Pass for beautify *)if!Flags.beautifythenbeautify_pass~doc:ostate.State.doc~comments~ids:(List.revids)~filename;(* End pass *)ostate