diff options
author | Maxime Dénès <mail@maximedenes.fr> | 2017-04-12 18:35:21 +0200 |
---|---|---|
committer | Maxime Dénès <mail@maximedenes.fr> | 2017-04-12 18:35:21 +0200 |
commit | a5c150a6a7fa980c5850aa247e62d02e29773235 (patch) | |
tree | e8f9a6211c3626d80d8427887bf181d10a3476f9 | |
parent | a74d64efb554e9fd57b8ec97fca7677033cc4fc4 (diff) | |
parent | b63b9a86b09856262b5b7bb2b3bb01f704032d41 (diff) |
Merge PR#441: Port Toplevel to the Stm API
-rw-r--r-- | dev/doc/changes.txt | 30 | ||||
-rw-r--r-- | ide/coqOps.ml | 12 | ||||
-rw-r--r-- | ide/ide_slave.ml | 75 | ||||
-rw-r--r-- | ide/interface.mli | 6 | ||||
-rw-r--r-- | ide/xmlprotocol.ml | 10 | ||||
-rw-r--r-- | interp/constrextern.ml | 16 | ||||
-rw-r--r-- | kernel/term_typing.ml | 2 | ||||
-rw-r--r-- | lib/feedback.ml | 10 | ||||
-rw-r--r-- | lib/feedback.mli | 23 | ||||
-rw-r--r-- | lib/flags.ml | 9 | ||||
-rw-r--r-- | lib/flags.mli | 26 | ||||
-rw-r--r-- | plugins/ltac/profile_ltac.ml | 2 | ||||
-rw-r--r-- | printing/printer.ml | 17 | ||||
-rw-r--r-- | stm/asyncTaskQueue.ml | 2 | ||||
-rw-r--r-- | stm/stm.ml | 385 | ||||
-rw-r--r-- | stm/stm.mli | 57 | ||||
-rw-r--r-- | toplevel/coqinit.ml | 11 | ||||
-rw-r--r-- | toplevel/coqinit.mli | 2 | ||||
-rw-r--r-- | toplevel/coqloop.ml | 59 | ||||
-rw-r--r-- | toplevel/coqloop.mli | 2 | ||||
-rw-r--r-- | toplevel/coqtop.ml | 39 | ||||
-rw-r--r-- | toplevel/vernac.ml | 182 | ||||
-rw-r--r-- | toplevel/vernac.mli | 26 | ||||
-rw-r--r-- | vernac/vernac.mllib | 1 | ||||
-rw-r--r-- | vernac/vernacentries.ml | 40 | ||||
-rw-r--r-- | vernac/vernacentries.mli | 32 | ||||
-rw-r--r-- | vernac/vernacprop.ml | 53 | ||||
-rw-r--r-- | vernac/vernacprop.mli | 19 |
28 files changed, 540 insertions, 608 deletions
diff --git a/dev/doc/changes.txt b/dev/doc/changes.txt index 2080f164a..7f915b781 100644 --- a/dev/doc/changes.txt +++ b/dev/doc/changes.txt @@ -146,6 +146,36 @@ document type". This allows for a more uniform handling of printing module Tag +** Stm API ** + +- We have streamlined the `Stm` API, now `add` and `query` take a + `coq_parsable` instead a `string` so clients can have more control + over their input stream. As a consequence, their types have been + modified. + +- The main parsing entry point has also been moved to the + `Stm`. Parsing is considered a synchronous operation so it will + either succeed or raise an exception. + +- `Feedback` is now only emitted for asynchronous operations. As a + consequence, it always carries a valid stateid and the type has + changed to accommodate that. + +- A few unused hooks were removed due to cleanups, no clients known. + +** Toplevel and Vernacular API ** + +- The components related to vernacular interpretation have been moved + to their own folder `vernac/` whereas toplevel now contains the + proper toplevel shell and compiler. + +- Coq's toplevel has been ported to directly use the common `Stm` + API. The signature of a few functions has changed as a result. + +** XML Protocol ** + +- The legacy `Interp` call has been turned into a noop. + ========================================= = CHANGES BETWEEN COQ V8.5 AND COQ V8.6 = ========================================= diff --git a/ide/coqOps.ml b/ide/coqOps.ml index 3d6a2583f..eb97755fa 100644 --- a/ide/coqOps.ml +++ b/ide/coqOps.ml @@ -414,11 +414,7 @@ object(self) buffer#apply_tag Tags.Script.tooltip ~start ~stop; add_tooltip sentence pre post markup - method private is_dummy_id id = - match id with - | Edit 0 -> true - | State id when Stateid.equal id Stateid.dummy -> true - | _ -> false + method private is_dummy_id id = Stateid.equal id Stateid.dummy method private enqueue_feedback msg = (* Minilib.log ("Feedback received: " ^ Xml_printer.to_string_fmt (Xmlprotocol.of_feedback msg)); *) @@ -433,8 +429,7 @@ object(self) let sentence = let finder _ state_id s = match state_id, id with - | Some id', State id when Stateid.equal id id' -> Some (state_id, s) - | _, Edit id when id = s.edit_id -> Some (state_id, s) + | Some id', id when Stateid.equal id id' -> Some (state_id, s) | _ -> None in try Some (Doc.find_map document finder) with Not_found -> None in @@ -503,8 +498,7 @@ object(self) else try match id, Doc.tip document with - | Edit _, _ -> () - | State id1, id2 when Stateid.newer_than id2 id1 -> () + | id1, id2 when Stateid.newer_than id2 id1 -> () | _ -> Queue.add msg feedbacks with Doc.Empty | Invalid_argument _ -> Queue.add msg feedbacks end; diff --git a/ide/ide_slave.ml b/ide/ide_slave.ml index 0dfa03cca..4b95e983d 100644 --- a/ide/ide_slave.ml +++ b/ide/ide_slave.ml @@ -8,6 +8,7 @@ (************************************************************************) open Vernacexpr +open Vernacprop open CErrors open Util open Pp @@ -60,25 +61,6 @@ let is_known_option cmd = match cmd with | VernacUnsetOption o -> coqide_known_option o | _ -> false -let is_debug cmd = match cmd with - | VernacSetOption (["Ltac";"Debug"], _) -> true - | _ -> false - -let is_query cmd = match cmd with - | VernacChdir None - | VernacMemOption _ - | VernacPrintOption _ - | VernacCheckMayEval _ - | VernacGlobalCheck _ - | VernacPrint _ - | VernacSearch _ - | VernacLocate _ -> true - | _ -> false - -let is_undo cmd = match cmd with - | VernacUndo _ | VernacUndoTo _ -> true - | _ -> false - (** Check whether a command is forbidden in the IDE *) let ide_cmd_checks (loc,ast) = @@ -86,16 +68,19 @@ let ide_cmd_checks (loc,ast) = if is_debug ast then user_error "Debug mode not available in the IDE"; if is_known_option ast then - Feedback.msg_warning (strbrk "Set this option from the IDE menu instead"); - if Vernac.is_navigation_vernac ast || is_undo ast then - Feedback.msg_warning (strbrk "Use IDE navigation instead"); + Feedback.msg_warning ~loc (strbrk "Set this option from the IDE menu instead"); + if is_navigation_vernac ast || is_undo ast then + Feedback.msg_warning ~loc (strbrk "Use IDE navigation instead"); if is_query ast then - Feedback.msg_warning (strbrk "Query commands should not be inserted in scripts") + Feedback.msg_warning ~loc (strbrk "Query commands should not be inserted in scripts") (** Interpretation (cf. [Ide_intf.interp]) *) let add ((s,eid),(sid,verbose)) = - let newid, rc = Stm.add ~ontop:sid verbose ~check:ide_cmd_checks eid s in + let pa = Pcoq.Gram.parsable (Stream.of_string s) in + let loc_ast = Stm.parse_sentence sid pa in + ide_cmd_checks loc_ast; + let newid, rc = Stm.add ~ontop:sid verbose loc_ast in let rc = match rc with `NewTip -> CSig.Inl () | `Unfocus id -> CSig.Inr id in (* TODO: the "" parameter is a leftover of the times the protocol * used to include stderr/stdout output. @@ -122,12 +107,14 @@ let edit_at id = * as not to break the core protocol for this minor change, but it should * be removed in the next version of the protocol. *) -let query (s,id) = Stm.query ~at:id s; "" +let query (s,id) = + let pa = Pcoq.Gram.parsable (Stream.of_string s) in + Stm.query ~at:id pa; "" let annotate phrase = let (loc, ast) = let pa = Pcoq.Gram.parsable (Stream.of_string phrase) in - Vernac.parse_sentence (pa,None) + Stm.parse_sentence (Stm.get_current_state ()) pa in (* XXX: Width should be a parameter of annotate... *) Richpp.richpp_of_pp 78 (Ppvernac.pr_vernac ast) @@ -370,43 +357,28 @@ let init = fun file -> if !initialized then anomaly (str "Already initialized") else begin + let init_sid = Stm.get_current_state () in initialized := true; match file with - | None -> Stm.get_current_state () + | None -> init_sid | Some file -> let dir = Filename.dirname file in let open Loadpath in let open CUnix in let initial_id, _ = - if not (is_in_load_paths (physical_path_of_string dir)) then - Stm.add false ~ontop:(Stm.get_current_state ()) - 0 (Printf.sprintf "Add LoadPath \"%s\". " dir) - else Stm.get_current_state (), `NewTip in + if not (is_in_load_paths (physical_path_of_string dir)) then begin + let pa = Pcoq.Gram.parsable (Stream.of_string (Printf.sprintf "Add LoadPath \"%s\". " dir)) in + let loc_ast = Stm.parse_sentence init_sid pa in + Stm.add false ~ontop:init_sid loc_ast + end else init_sid, `NewTip in if Filename.check_suffix file ".v" then Stm.set_compilation_hints file; Stm.finish (); initial_id end -(* Retrocompatibility stuff *) +(* Retrocompatibility stuff, disabled since 8.7 *) let interp ((_raw, verbose), s) = - let vernac_parse s = - let pa = Pcoq.Gram.parsable (Stream.of_string s) in - Flags.with_option Flags.we_are_parsing (fun () -> - match Pcoq.Gram.entry_parse Pcoq.main_entry pa with - | None -> raise (Invalid_argument "vernac_parse") - | Some ast -> ast) - () in - Stm.interp verbose (vernac_parse s); - (* TODO: the "" parameter is a leftover of the times the protocol - * used to include stderr/stdout output. - * - * Currently, we force all the output meant for the to go via the - * feedback mechanism, and we don't manipulate stderr/stdout, which - * are left to the client's discrection. The parameter is still there - * as not to break the core protocol for this minor change, but it should - * be removed in the next version of the protocol. - *) - Stm.get_current_state (), CSig.Inl "" + Stateid.dummy, CSig.Inr "The interp call has been disabled, please use Add." (** When receiving the Quit call, we don't directly do an [exit 0], but rather set this reference, in order to send a final answer @@ -494,9 +466,6 @@ let loop () = let xml_ic = Xml_parser.make (Xml_parser.SLexbuf in_lb) in let () = Xml_parser.check_eof xml_ic false in ignore (Feedback.add_feeder (slave_feeder (!msg_format ()) xml_oc)); - (* We'll handle goal fetching and display in our own way *) - Vernacentries.enable_goal_printing := false; - Vernacentries.qed_display_script := false; while not !quit do try let xml_query = Xml_parser.parse xml_ic in diff --git a/ide/interface.mli b/ide/interface.mli index 9ed606258..62f63aefb 100644 --- a/ide/interface.mli +++ b/ide/interface.mli @@ -111,8 +111,10 @@ type coq_info = { (** Calls result *) type location = (int * int) option (* start and end of the error *) -type state_id = Feedback.state_id -type edit_id = Feedback.edit_id +type state_id = Stateid.t + +(* Obsolete *) +type edit_id = int (* The fail case carries the current state_id of the prover, the GUI should probably retract to that point *) diff --git a/ide/xmlprotocol.ml b/ide/xmlprotocol.ml index d7950e5fd..bf52b0b52 100644 --- a/ide/xmlprotocol.ml +++ b/ide/xmlprotocol.ml @@ -907,9 +907,7 @@ let of_feedback_content = function of_string filename ] | Message (l,loc,m) -> constructor "feedback_content" "message" [ of_message l loc m ] -let of_edit_or_state_id = function - | Edit id -> ["object","edit"], of_edit_id id - | State id -> ["object","state"], of_stateid id +let of_edit_or_state_id id = ["object","state"], of_stateid id let of_feedback msg = let content = of_feedback_content msg.contents in @@ -921,12 +919,8 @@ let of_feedback msg_fmt = msg_format := msg_fmt; of_feedback let to_feedback xml = match xml with - | Element ("feedback", ["object","edit";"route",route], [id;content]) -> { - id = Edit(to_edit_id id); - route = int_of_string route; - contents = to_feedback_content content } | Element ("feedback", ["object","state";"route",route], [id;content]) -> { - id = State(to_stateid id); + id = to_stateid id; route = int_of_string route; contents = to_feedback_content content } | x -> raise (Marshal_error("feedback",x)) diff --git a/interp/constrextern.ml b/interp/constrextern.ml index f272d219a..59b8b4e5b 100644 --- a/interp/constrextern.ml +++ b/interp/constrextern.ml @@ -85,6 +85,20 @@ let without_specific_symbols l f = (**********************************************************************) (* Control printing of records *) +(* Set Record Printing flag *) +let record_print = ref true + +let _ = + let open Goptions in + declare_bool_option + { optsync = true; + optdepr = false; + optname = "record printing"; + optkey = ["Printing";"Records"]; + optread = (fun () -> !record_print); + optwrite = (fun b -> record_print := b) } + + let is_record indsp = try let _ = Recordops.lookup_structure indsp in @@ -658,7 +672,7 @@ let rec extern inctx scopes vars r = () else if PrintingConstructor.active (fst cstrsp) then raise Exit - else if not !Flags.record_print then + else if not !record_print then raise Exit; let projs = struc.Recordops.s_PROJ in let locals = struc.Recordops.s_PROJKIND in diff --git a/kernel/term_typing.ml b/kernel/term_typing.ml index f43b01d1b..eeb9c421a 100644 --- a/kernel/term_typing.ml +++ b/kernel/term_typing.ml @@ -223,7 +223,7 @@ let rec unzip ctx j = let feedback_completion_typecheck = let open Feedback in Option.iter (fun state_id -> - feedback ~id:(State state_id) Feedback.Complete) + feedback ~id:state_id Feedback.Complete) let infer_declaration ~trust env kn dcl = match dcl with diff --git a/lib/feedback.ml b/lib/feedback.ml index 7d9d6bf7f..df6fe3a62 100644 --- a/lib/feedback.ml +++ b/lib/feedback.ml @@ -15,9 +15,6 @@ type level = | Warning | Error -type edit_id = int -type state_id = Stateid.t -type edit_or_state_id = Edit of edit_id | State of state_id type route_id = int type feedback_content = @@ -38,9 +35,9 @@ type feedback_content = | Message of level * Loc.t option * Pp.std_ppcmds type feedback = { - id : edit_or_state_id; + id : Stateid.t; + route : route_id; contents : feedback_content; - route : route_id; } let default_route = 0 @@ -56,7 +53,8 @@ let add_feeder = let del_feeder fid = Hashtbl.remove feeders fid -let feedback_id = ref (Edit 0) +let default_route = 0 +let feedback_id = ref Stateid.dummy let feedback_route = ref default_route let set_id_for_feedback ?(route=default_route) i = diff --git a/lib/feedback.mli b/lib/feedback.mli index 4bbdfcb5b..bdd236ac7 100644 --- a/lib/feedback.mli +++ b/lib/feedback.mli @@ -16,11 +16,8 @@ type level = | Warning | Error -(** Coq "semantic" infos obtained during parsing/execution *) -type edit_id = int -type state_id = Stateid.t -type edit_or_state_id = Edit of edit_id | State of state_id +(** Coq "semantic" infos obtained during execution *) type route_id = int val default_route : route_id @@ -46,17 +43,16 @@ type feedback_content = | Message of level * Loc.t option * Pp.std_ppcmds type feedback = { - id : edit_or_state_id; (* The document part concerned *) - contents : feedback_content; (* The payload *) + id : Stateid.t; (* The document part concerned *) route : route_id; (* Extra routing info *) + contents : feedback_content; (* The payload *) } (** {6 Feedback sent, even asynchronously, to the user interface} *) -(* Morally the parser gets a string and an edit_id, and gives back an AST. - * Feedbacks during the parsing phase are attached to this edit_id. - * The interpreter assignes an exec_id to the ast, and feedbacks happening - * during interpretation are attached to the exec_id. - * Only one among state_id and edit_id can be provided. *) + +(* The interpreter assignes an state_id to the ast, and feedbacks happening + * during interpretation are attached to it. + *) (** [add_feeder f] adds a feeder listiner [f], returning its id *) val add_feeder : (feedback -> unit) -> int @@ -67,11 +63,10 @@ val del_feeder : int -> unit (** [feedback ?id ?route fb] produces feedback fb, with [route] and [id] set appropiatedly, if absent, it will use the defaults set by [set_id_for_feedback] *) -val feedback : - ?id:edit_or_state_id -> ?route:route_id -> feedback_content -> unit +val feedback : ?id:Stateid.t -> ?route:route_id -> feedback_content -> unit (** [set_id_for_feedback route id] Set the defaults for feedback *) -val set_id_for_feedback : ?route:route_id -> edit_or_state_id -> unit +val set_id_for_feedback : ?route:route_id -> Stateid.t -> unit (** {6 output functions} diff --git a/lib/flags.ml b/lib/flags.ml index 5b080151c..d87d9e729 100644 --- a/lib/flags.ml +++ b/lib/flags.ml @@ -86,8 +86,6 @@ let in_toplevel = ref false let profile = false let print_emacs = ref false -let coqtop_ui = ref false - let xml_export = ref false let ide_slave = ref false @@ -97,7 +95,6 @@ let time = ref false let raw_print = ref false -let record_print = ref true let univ_print = ref false @@ -183,12 +180,6 @@ let warn = ref true let make_warn flag = warn := flag; () let if_warn f x = if !warn then f x -(* The number of printed hypothesis in a goal *) - -let print_hyps_limit = ref (None : int option) -let set_print_hyps_limit n = print_hyps_limit := n -let print_hyps_limit () = !print_hyps_limit - (* Flags for external tools *) let browser_cmd_fmt = diff --git a/lib/flags.mli b/lib/flags.mli index bd365e653..c5c4a15aa 100644 --- a/lib/flags.mli +++ b/lib/flags.mli @@ -8,6 +8,8 @@ (** Global options of the system. *) +(** Command-line flags *) + val boot : bool ref val load_init : bool ref @@ -16,8 +18,11 @@ type compilation_mode = BuildVo | BuildVio | Vio2Vo val compilation_mode : compilation_mode ref val compilation_output_name : string option ref +(* Flag set when the test-suite is called. Its only effect to display + verbose information for `Fail` *) val test_mode : bool ref +(** Async-related flags *) type async_proofs = APoff | APonLazy | APon val async_proofs_mode : async_proofs ref type cache = Force @@ -46,20 +51,27 @@ val in_toplevel : bool ref val profile : bool -val print_emacs : bool ref -val coqtop_ui : bool ref +(* Legacy flags *) +(* -emacs option: printing includes emacs tags, will affect stm caching. *) +val print_emacs : bool ref +(* -xml option: xml hooks will be called *) val xml_export : bool ref +(* -ide_slave: printing will be more verbose, will affect stm caching *) val ide_slave : bool ref val ideslave_coqtop_flags : string option ref +(* -time option: every command will be wrapped with `Time` *) val time : bool ref +(* development flag to detect race conditions, it should go away. *) val we_are_parsing : bool ref +(* Set Printing All flag. For some reason it is a global flag *) val raw_print : bool ref -val record_print : bool ref + +(* Univ print flag, never set anywere. Maybe should belong to Univ? *) val univ_print : bool ref type compat_version = V8_2 | V8_3 | V8_4 | V8_5 | V8_6 | Current @@ -69,9 +81,12 @@ val version_strictly_greater : compat_version -> bool val version_less_or_equal : compat_version -> bool val pr_version : compat_version -> string +(* Beautify command line flags, should move to printing? *) val beautify : bool ref val beautify_file : bool ref +(* Silent/verbose, both actually controlled by a single flag so they + are mutually exclusive *) val make_silent : bool -> unit val is_silent : unit -> bool val is_verbose : unit -> bool @@ -80,6 +95,7 @@ val verbosely : ('a -> 'b) -> 'a -> 'b val if_silent : ('a -> unit) -> 'a -> unit val if_verbose : ('a -> unit) -> 'a -> unit +(* Miscellaneus flags for vernac *) val make_auto_intros : bool -> unit val is_auto_intros : unit -> bool @@ -111,10 +127,6 @@ val without_option : bool ref -> ('a -> 'b) -> 'a -> 'b (** Temporarily extends the reference to a list *) val with_extra_values : 'c list ref -> 'c list -> ('a -> 'b) -> 'a -> 'b -(** If [None], no limit *) -val set_print_hyps_limit : int option -> unit -val print_hyps_limit : unit -> int option - (** Options for external tools *) (** Returns string format for default browser to use from Coq or CoqIDE *) diff --git a/plugins/ltac/profile_ltac.ml b/plugins/ltac/profile_ltac.ml index 58123f63e..bcb28f77c 100644 --- a/plugins/ltac/profile_ltac.ml +++ b/plugins/ltac/profile_ltac.ml @@ -374,7 +374,7 @@ let data = ref SM.empty let _ = Feedback.(add_feeder (function - | { id = State s; contents = Custom (_, "ltacprof_results", xml) } -> + | { id = s; contents = Custom (_, "ltacprof_results", xml) } -> let results = to_ltacprof_results xml in let other_results = (* Multi success can cause this *) try SM.find s !data diff --git a/printing/printer.ml b/printing/printer.ml index 239e1d7eb..35ddf2e8c 100644 --- a/printing/printer.ml +++ b/printing/printer.ml @@ -375,8 +375,21 @@ let pr_context_limit_compact ?n env sigma = let pr_context_unlimited_compact env sigma = pr_context_limit_compact env sigma -let pr_context_of env sigma = - match Flags.print_hyps_limit () with +(* The number of printed hypothesis in a goal *) +(* If [None], no limit *) +let print_hyps_limit = ref (None : int option) + +let _ = + let open Goptions in + declare_int_option + { optsync = false; + optdepr = false; + optname = "the hypotheses limit"; + optkey = ["Hyps";"Limit"]; + optread = (fun () -> !print_hyps_limit); + optwrite = (fun x -> print_hyps_limit := x) } + +let pr_context_of env sigma = match !print_hyps_limit with | None -> hv 0 (pr_context_limit_compact env sigma) | Some n -> hv 0 (pr_context_limit_compact ~n env sigma) diff --git a/stm/asyncTaskQueue.ml b/stm/asyncTaskQueue.ml index 125491988..3459156a4 100644 --- a/stm/asyncTaskQueue.ml +++ b/stm/asyncTaskQueue.ml @@ -105,7 +105,7 @@ module Make(T : Task) = struct let report_status ?(id = !Flags.async_proofs_worker_id) s = let open Feedback in - feedback ~id:(State Stateid.initial) (WorkerStatus(id, s)) + feedback ~id:Stateid.initial (WorkerStatus(id, s)) module Worker = Spawn.Sync(struct end) diff --git a/stm/stm.ml b/stm/stm.ml index 2b6ee5511..fd264e404 100644 --- a/stm/stm.ml +++ b/stm/stm.ml @@ -6,34 +6,32 @@ (* * GNU Lesser General Public License Version 2.1 *) (************************************************************************) +(* enable in case of stm problems *) +let stm_debug = false + let stm_pr_err s = Printf.eprintf "%s] %s\n" (System.process_id ()) s; flush stderr let stm_pp_err pp = Format.eprintf "%s] @[%a@]\n" (System.process_id ()) Pp.pp_with pp; flush stderr -let stm_prerr_endline s = if false then begin stm_pr_err (s ()) end else () -let stm_prerr_debug s = if !Flags.debug then begin stm_pr_err (s ()) end else () +let stm_prerr_endline s = if stm_debug then begin stm_pr_err (s ()) end else () +let stm_pperr_endline s = if stm_debug then begin stm_pp_err (s ()) end else () -let stm_pperr_endline s = if false then begin stm_pp_err (s ()) end else () +let stm_prerr_debug s = if !Flags.debug then begin stm_pr_err (s ()) end else () -open Vernacexpr -open CErrors open Pp -open Names -open Util -open Ppvernac -open Vernac_classifier +open CErrors open Feedback +open Vernacexpr +open Vernac_classifier let execution_error state_id loc msg = - feedback ~id:(State state_id) + feedback ~id:state_id (Message (Error, Some loc, msg)) module Hooks = struct -let process_error, process_error_hook = Hook.make () - let state_computed, state_computed_hook = Hook.make ~default:(fun state_id ~in_cache -> - feedback ~id:(State state_id) Processed) () + feedback ~id:state_id Processed) () let state_ready, state_ready_hook = Hook.make ~default:(fun state_id -> ()) () @@ -45,16 +43,9 @@ let forward_feedback, forward_feedback_hook = try Mutex.lock m; feedback ~id:id ~route contents; Mutex.unlock m with e -> Mutex.unlock m; raise e) () -let parse_error, parse_error_hook = Hook.make - ~default:(fun id loc msg -> - feedback ~id (Message(Error, Some loc, msg))) () - let unreachable_state, unreachable_state_hook = Hook.make ~default:(fun _ _ -> ()) () -let tactic_being_run, tactic_being_run_hook = Hook.make - ~default:(fun _ -> ()) () - include Hook (* enables: Hooks.(call foo args) *) @@ -66,7 +57,7 @@ let call_process_error_once = match Exninfo.get info processed with | Some _ -> ei | None -> - let e, info = call process_error ei in + let e, info = ExplainErr.process_vernac_interp_error ei in let info = Exninfo.add info processed () in e, info @@ -86,7 +77,7 @@ type aast = { strlen : int; mutable expr : vernac_expr; (* mutable: Proof using hinted by aux file *) } -let pr_ast { expr; indentation } = int indentation ++ str " " ++ pr_vernac expr +let pr_ast { expr; indentation } = Pp.(int indentation ++ str " " ++ Ppvernac.pr_vernac expr) let default_proof_mode () = Proof_global.get_default_proof_mode_name () @@ -102,43 +93,6 @@ let may_pierce_opaque = function | { expr = VernacExtend (("ExtractionInductive",_), _) } -> true | _ -> false -(* Wrapper for Vernac.parse_sentence to set the feedback id *) -let indentation_of_string s = - let len = String.length s in - let rec aux n i precise = - if i >= len then 0, precise, len - else - match s.[i] with - | ' ' | '\t' -> aux (succ n) (succ i) precise - | '\n' | '\r' -> aux 0 (succ i) true - | _ -> n, precise, len in - aux 0 0 false - -let vernac_parse ?(indlen_prev=fun() -> 0) ?newtip ?route eid s = - let feedback_id = - if Option.is_empty newtip then Edit eid - else State (Option.get newtip) in - let indentation, precise, strlen = indentation_of_string s in - let indentation = - if precise then indentation else indlen_prev () + indentation in - set_id_for_feedback ?route feedback_id; - let pa = Pcoq.Gram.parsable (Stream.of_string s) in - Flags.with_option Flags.we_are_parsing (fun () -> - try - match Pcoq.Gram.entry_parse Pcoq.main_entry pa with - | None -> raise (Invalid_argument "vernac_parse") - | Some (loc, ast) -> indentation, strlen, loc, ast - with e when CErrors.noncritical e -> - let (e, info) = CErrors.push e in - let loc = Option.default Loc.ghost (Loc.get_loc info) in - Hooks.(call parse_error feedback_id loc (iprint (e, info))); - iraise (e, info)) - () - -let pr_open_cur_subgoals () = - try Printer.pr_open_subgoals () - with Proof_global.NoCurrentProof -> Pp.str "" - let update_global_env () = if Proof_global.there_are_pending_proofs () then Proof_global.update_global_env () @@ -158,13 +112,13 @@ type cmd_t = { ctac : bool; (* is a tactic *) ceff : bool; (* is a side-effecting command in the middle of a proof *) cast : aast; - cids : Id.t list; + cids : Names.Id.t list; cblock : proof_block_name option; cqueue : [ `MainQueue | `TacQueue of solving_tac * anon_abstracting_tac * cancel_switch | `QueryQueue of cancel_switch | `SkipQueue ] } -type fork_t = aast * Vcs_.Branch.t * Vernacexpr.opacity_guarantee * Id.t list +type fork_t = aast * Vcs_.Branch.t * Vernacexpr.opacity_guarantee * Names.Id.t list type qed_t = { qast : aast; keep : vernac_qed_type; @@ -253,7 +207,7 @@ end = struct (* {{{ *) let proof_nesting vcs = List.fold_left max 0 - (List.map_filter + (CList.map_filter (function | { Vcs_.kind = `Proof (_,n) } -> Some n | { Vcs_.kind = `Edit _ } -> Some 1 @@ -369,6 +323,15 @@ end = struct (* {{{ *) open Printf let print_dag vcs () = + + (* Due to threading, be wary that this will be called from the + toplevel with we_are_parsing set to true, as indeed, the + toplevel is waiting for input . What a race! XD + + In case you are hitting the race enable stm_debug. + *) + if stm_debug then Flags.we_are_parsing := false; + let fname = "stm_" ^ Str.global_replace (Str.regexp " ") "_" (System.process_id ()) in let string_of_transaction = function @@ -380,7 +343,7 @@ end = struct (* {{{ *) | Sideff None -> "EnvChange" | Noop -> " " | Alias (id,_) -> sprintf "Alias(%s)" (Stateid.to_string id) - | Qed { qast } -> string_of_ppcmds (pr_ast qast) in + | Qed { qast } -> Pp.string_of_ppcmds (pr_ast qast) in let is_green id = match get_info vcs id with | Some { state = Valid _ } -> true @@ -510,8 +473,8 @@ end = struct (* {{{ *) let reachable id = reachable !vcs id let mk_branch_name { expr = x } = Branch.make (match x with - | VernacDefinition (_,((_,i),_),_) -> string_of_id i - | VernacStartTheoremProof (_,[Some ((_,i),_),_],_) -> string_of_id i + | VernacDefinition (_,((_,i),_),_) -> Names.string_of_id i + | VernacStartTheoremProof (_,[Some ((_,i),_),_],_) -> Names.string_of_id i | _ -> "branch") let edit_branch = Branch.make "edit" let branch ?root ?pos name kind = vcs := branch !vcs ?root ?pos name kind @@ -567,7 +530,7 @@ end = struct (* {{{ *) | { next = n; step = `Alias x } -> (id,Alias x) :: aux n | { next = n; step = `Sideff (`Ast (x,_)) } -> (id,Sideff (Some x)) :: aux n - | _ -> anomaly(str("Cannot slice from "^ Stateid.to_string block_start ^ + | _ -> anomaly Pp.(str("Cannot slice from "^ Stateid.to_string block_start ^ " to "^Stateid.to_string block_stop)) in aux block_stop @@ -620,11 +583,11 @@ end = struct (* {{{ *) l let create_proof_task_box l ~qed ~block_start:lemma = - if not (topo_invariant l) then anomaly (str "overlapping boxes"); + if not (topo_invariant l) then anomaly Pp.(str "overlapping boxes"); vcs := create_property !vcs l (ProofTask { qed; lemma }) let create_proof_block ({ block_start; block_stop} as decl) name = let l = nodes_in_slice ~block_start ~block_stop in - if not (topo_invariant l) then anomaly (str "overlapping boxes"); + if not (topo_invariant l) then anomaly Pp.(str "overlapping boxes"); vcs := create_property !vcs l (ProofBlock (decl, name)) let box_of id = List.map Dag.Property.data (property_of !vcs id) let delete_boxes_of id = @@ -635,7 +598,7 @@ end = struct (* {{{ *) with | [] -> None | [x] -> Some x - | _ -> anomaly (str "node with more than 1 proof task box") + | _ -> anomaly Pp.(str "node with more than 1 proof task box") let gc () = let old_vcs = !vcs in @@ -716,14 +679,14 @@ module State : sig ?safe_id:Stateid.t -> ?redefine:bool -> ?cache:Summary.marshallable -> ?feedback_processed:bool -> (unit -> unit) -> Stateid.t -> unit - val fix_exn_ref : (iexn -> iexn) ref + val fix_exn_ref : (Exninfo.iexn -> Exninfo.iexn) ref val install_cached : Stateid.t -> unit val is_cached : ?cache:Summary.marshallable -> Stateid.t -> bool val is_cached_and_valid : ?cache:Summary.marshallable -> Stateid.t -> bool - val exn_on : Stateid.t -> valid:Stateid.t -> iexn -> iexn + val exn_on : Stateid.t -> valid:Stateid.t -> Exninfo.iexn -> Exninfo.iexn (* to send states across worker/master *) type frozen_state val get_cached : Stateid.t -> frozen_state @@ -736,6 +699,9 @@ module State : sig val proof_part_of_frozen : frozen_state -> proof_part val assign : Stateid.t -> partial_state -> unit + (* Only for internal use to catch problems in parse_sentence, should + be removed in the state handling refactoring. *) + val cur_id : Stateid.t ref end = struct (* {{{ *) (* cur_id holds Stateid.dummy in case the last attempt to define a state @@ -796,13 +762,13 @@ end = struct (* {{{ *) | _ -> (* coqc has a 1 slot cache and only for valid states *) if interactive () = `No && Stateid.equal id !cur_id then () - else anomaly (str "installing a non cached state") + else anomaly Pp.(str "installing a non cached state") let get_cached id = try match VCS.get_info id with | { state = Valid s } -> s - | _ -> anomaly (str "not a cached state") - with VCS.Expired -> anomaly (str "not a cached state (expired)") + | _ -> anomaly Pp.(str "not a cached state") + with VCS.Expired -> anomaly Pp.(str "not a cached state (expired)") let assign id what = if VCS.get_state id <> Empty then () else @@ -850,10 +816,10 @@ end = struct (* {{{ *) let define ?safe_id ?(redefine=false) ?(cache=`No) ?(feedback_processed=true) f id = - feedback ~id:(State id) (ProcessingIn !Flags.async_proofs_worker_id); + feedback ~id:id (ProcessingIn !Flags.async_proofs_worker_id); let str_id = Stateid.to_string id in if is_cached id && not redefine then - anomaly (str"defining state "++str str_id++str" twice"); + anomaly Pp.(str"defining state "++str str_id++str" twice"); try stm_prerr_endline (fun () -> "defining "^str_id^" (cache="^ if cache = `Yes then "Y)" else if cache = `Shallow then "S)" else "N)"); @@ -928,9 +894,9 @@ let indent_script_item ((ng1,ngl1),nl,beginend,ppl) (cmd,ng) = | VernacBullet _ -> pred ind, nl, beginend | _ -> ind, nl, beginend in - let pp = + let pp = Pp.( (if nl then fnl () else mt ()) ++ - (hov (ind+1) (str (String.make ind ' ') ++ Ppvernac.pr_vernac cmd)) + (hov (ind+1) (str (String.make ind ' ') ++ Ppvernac.pr_vernac cmd))) in (new_ngl, new_nl, new_beginend, pp :: ppl) @@ -970,7 +936,7 @@ let show_script ?proof () = List.fold_left indent_script_item ((1,[]),false,[],[]) cmds in let indented_cmds = List.rev (indented_cmds) in - msg_notice (v 0 (prlist_with_sep fnl (fun x -> x) indented_cmds)) + msg_notice Pp.(v 0 (prlist_with_sep fnl (fun x -> x) indented_cmds)) with Vcs_aux.Expired -> () end @@ -982,7 +948,7 @@ let stm_vernac_interp ?proof id ?route { verbose; loc; expr } = (* The Stm will gain the capability to interpret commmads affecting the whole document state, such as backtrack, etc... so we start to design the stm command interpreter now *) - set_id_for_feedback ?route (State id); + set_id_for_feedback ?route id; Aux_file.record_in_aux_set_at loc; (* We need to check if a command should be filtered from * vernac_entries, as it cannot handle it. This should go away in @@ -997,15 +963,15 @@ let stm_vernac_interp ?proof id ?route { verbose; loc; expr } = in let aux_interp cmd = if is_filtered_command cmd then - stm_pperr_endline Pp.(fun () -> str "ignoring " ++ pr_vernac expr) + stm_pperr_endline Pp.(fun () -> str "ignoring " ++ Ppvernac.pr_vernac expr) else match cmd with | VernacShow ShowScript -> ShowScript.show_script () | expr -> - stm_pperr_endline Pp.(fun () -> str "interpreting " ++ pr_vernac expr); + stm_pperr_endline Pp.(fun () -> str "interpreting " ++ Ppvernac.pr_vernac expr); try Vernacentries.interp ?verbosely:(Some verbose) ?proof (loc, expr) with e -> let e = CErrors.push e in - iraise Hooks.(call_process_error_once e) + Exninfo.iraise Hooks.(call_process_error_once e) in aux_interp expr (****************************** CRUFT *****************************************) @@ -1016,7 +982,6 @@ module Backtrack : sig val record : unit -> unit val backto : Stateid.t -> unit - val back_safe : unit -> unit (* we could navigate the dag, but this ways easy *) val branches_of : Stateid.t -> backup @@ -1045,7 +1010,7 @@ end = struct (* {{{ *) let info = VCS.get_info oid in match info.vcs_backup with | None, _ -> - anomaly(str"Backtrack.backto "++str(Stateid.to_string oid)++ + anomaly Pp.(str"Backtrack.backto "++str(Stateid.to_string oid)++ str": a state with no vcs_backup") | Some vcs, _ -> VCS.restore vcs @@ -1053,8 +1018,8 @@ end = struct (* {{{ *) let info = VCS.get_info id in match info.vcs_backup with | _, None -> - anomaly(str"Backtrack.branches_of "++str(Stateid.to_string id)++ - str": a state with no vcs_backup") + anomaly Pp.(str"Backtrack.branches_of "++str(Stateid.to_string id)++ + str": a state with no vcs_backup") | _, Some x -> x let rec fold_until f acc id = @@ -1115,7 +1080,7 @@ end = struct (* {{{ *) let id = VCS.get_branch_pos (VCS.current_branch ()) in let vcs = match (VCS.get_info id).vcs_backup with - | None, _ -> anomaly(str"Backtrack: tip with no vcs_backup") + | None, _ -> anomaly Pp.(str"Backtrack: tip with no vcs_backup") | Some vcs, _ -> vcs in let cb, _ = try Vcs_aux.find_proof_at_depth vcs (Vcs_aux.proof_nesting vcs) @@ -1139,7 +1104,7 @@ end = struct (* {{{ *) with | Not_found -> CErrors.user_err ~hdr:"undo_vernac_classifier" - (str "Cannot undo") + Pp.(str "Cannot undo") end (* }}} *) @@ -1171,7 +1136,7 @@ let record_pb_time proof_name loc time = hints := Aux_file.set !hints Loc.ghost proof_name proof_build_time end -exception RemoteException of std_ppcmds +exception RemoteException of Pp.std_ppcmds let _ = CErrors.register_handler (function | RemoteException ppcmd -> ppcmd | _ -> raise Unhandled) @@ -1206,7 +1171,7 @@ let proof_block_delimiters = ref [] let register_proof_block_delimiter name static dynamic = if List.mem_assoc name !proof_block_delimiters then - CErrors.user_err ~hdr:"STM" (str "Duplicate block delimiter " ++ str name); + CErrors.user_err ~hdr:"STM" Pp.(str "Duplicate block delimiter " ++ str name); proof_block_delimiters := (name, (static,dynamic)) :: !proof_block_delimiters let mk_doc_node id = function @@ -1242,7 +1207,7 @@ let detect_proof_block id name = end with Not_found -> CErrors.user_err ~hdr:"STM" - (str "Unknown proof block delimiter " ++ str name) + Pp.(str "Unknown proof block delimiter " ++ str name) ) (****************************** THE SCHEDULER *********************************) (******************************************************************************) @@ -1309,7 +1274,7 @@ end = struct (* {{{ *) type error = { e_error_at : Stateid.t; e_safe_id : Stateid.t; - e_msg : std_ppcmds; + e_msg : Pp.std_ppcmds; e_safe_states : Stateid.t list } type response = @@ -1382,9 +1347,9 @@ end = struct (* {{{ *) | Some (BuildProof { t_start = start; t_assign }) -> let s = "Worker dies or task expired" in let info = Stateid.add ~valid:start Exninfo.null start in - let e = (RemoteException (strbrk s), info) in + let e = (RemoteException (Pp.strbrk s), info) in t_assign (`Exn e); - execution_error start Loc.ghost (strbrk s); + execution_error start Loc.ghost (Pp.strbrk s); feedback (InProgress ~-1) let build_proof_here ~drop_pt (id,valid) loc eop = @@ -1396,7 +1361,7 @@ end = struct (* {{{ *) Aux_file.record_in_aux_at loc "proof_build_time" (Printf.sprintf "%.3f" (wall_clock2 -. wall_clock1)); let p = Proof_global.return_proof ~allow_partial:drop_pt () in - if drop_pt then feedback ~id:(State id) Complete; + if drop_pt then feedback ~id Complete; p) let perform_buildp { Stateid.exn_info; stop; document; loc } drop my_states = @@ -1465,7 +1430,7 @@ end = struct (* {{{ *) when is_tac expr && State.same_env o n -> (* A pure tactic *) Some (id, `Proof (prev, State.proof_part_of_frozen n)) | Some _, Some s -> - msg_debug (str "STM: sending back a fat state"); + msg_debug (Pp.str "STM: sending back a fat state"); Some (id, `Full s) | _, Some s -> Some (id, `Full s) in let rec aux seen = function @@ -1481,10 +1446,10 @@ end = struct (* {{{ *) | ReqStates sl -> RespStates (perform_states sl) let on_marshal_error s = function - | States _ -> msg_error(strbrk("Marshalling error: "^s^". "^ + | States _ -> msg_error(Pp.strbrk("Marshalling error: "^s^". "^ "The system state could not be sent to the master process.")) | BuildProof { t_exn_info; t_stop; t_assign; t_loc; t_drop = drop_pt } -> - msg_error(strbrk("Marshalling error: "^s^". "^ + msg_error(Pp.strbrk("Marshalling error: "^s^". "^ "The system state could not be sent to the worker process. "^ "Falling back to local, lazy, evaluation.")); t_assign(`Comp(build_proof_here ~drop_pt t_exn_info t_loc t_stop)); @@ -1537,7 +1502,7 @@ end = struct (* {{{ *) let check_task_aux extra name l i = let { Stateid.stop; document; loc; name = r_name }, drop = List.nth l i in Flags.if_verbose msg_info - (str(Printf.sprintf "Checking task %d (%s%s) of %s" i r_name extra name)); + Pp.(str(Printf.sprintf "Checking task %d (%s%s) of %s" i r_name extra name)); VCS.restore document; let start = let rec aux cur = @@ -1568,7 +1533,7 @@ end = struct (* {{{ *) let (e, info) = CErrors.push e in (try match Stateid.get info with | None -> - msg_error ( + msg_error Pp.( str"File " ++ str name ++ str ": proof of " ++ str r_name ++ spc () ++ iprint (e, info)) | Some (_, cur) -> @@ -1578,16 +1543,16 @@ end = struct (* {{{ *) | { step = `Qed ( { qast = { loc } }, _) } | { step = `Sideff (`Ast ( { loc }, _)) } -> let start, stop = Loc.unloc loc in - msg_error ( + msg_error Pp.( str"File " ++ str name ++ str ": proof of " ++ str r_name ++ str ": chars " ++ int start ++ str "-" ++ int stop ++ spc () ++ iprint (e, info)) | _ -> - msg_error ( + msg_error Pp.( str"File " ++ str name ++ str ": proof of " ++ str r_name ++ spc () ++ iprint (e, info)) with e -> - msg_error (str"unable to print error message: " ++ + msg_error Pp.(str"unable to print error message: " ++ str (Printexc.to_string e))); if drop then `ERROR_ADMITTED else `ERROR @@ -1746,7 +1711,7 @@ end = struct (* {{{ *) type response = | RespBuiltSubProof of output - | RespError of std_ppcmds + | RespError of Pp.std_ppcmds | RespNoProgress exception NoProgress @@ -1806,7 +1771,7 @@ end = struct (* {{{ *) List.for_all (Context.Named.Declaration.for_all is_ground) Evd.(evar_context g)) then - CErrors.user_err ~hdr:"STM" (strbrk("the par: goal selector supports ground "^ + CErrors.user_err ~hdr:"STM" Pp.(strbrk("the par: goal selector supports ground "^ "goals only")) else begin let (i, ast) = r_ast in @@ -1821,7 +1786,7 @@ end = struct (* {{{ *) if Evarutil.is_ground_term sigma t then let t = EConstr.Unsafe.to_constr t in RespBuiltSubProof (t, Evd.evar_universe_context sigma) - else CErrors.user_err ~hdr:"STM" (str"The solution is not ground") + else CErrors.user_err ~hdr:"STM" Pp.(str"The solution is not ground") end) () with e when CErrors.noncritical e -> RespError (CErrors.print e) @@ -1943,11 +1908,11 @@ end = struct (* {{{ *) Reach.known_state ~cache:`No r_where; try stm_vernac_interp r_for { r_what with verbose = true }; - feedback ~id:(State r_for) Processed + feedback ~id:r_for Processed with e when CErrors.noncritical e -> let e = CErrors.push e in let msg = iprint e in - feedback ~id:(State r_for) (Message (Error, None, msg)) + feedback ~id:r_for (Message (Error, None, msg)) let name_of_task { t_what } = string_of_ppcmds (pr_ast t_what) let name_of_request { r_what } = string_of_ppcmds (pr_ast r_what) @@ -2009,7 +1974,7 @@ let collect_proof keep cur hd brkind id = let no_name = "" in let name = function | [] -> no_name - | id :: _ -> Id.to_string id in + | id :: _ -> Names.Id.to_string id in let loc = (snd cur).loc in let is_defined = function | _, { expr = VernacEndProof (Proved ((Transparent|Opaque (Some _)),_)) } -> @@ -2130,13 +2095,13 @@ let known_state ?(redefine_qed=false) ~cache id = Some (decl, name) | _ -> None) boxes in assert(List.length valid_boxes < 2); - if valid_boxes = [] then iraise exn + if valid_boxes = [] then Exninfo.iraise exn else let decl, name = List.hd valid_boxes in try let _, dynamic_check = List.assoc name !proof_block_delimiters in match dynamic_check decl with - | `Leaks -> iraise exn + | `Leaks -> Exninfo.iraise exn | `ValidBlock { base_state; goals_to_admit; recovery_command } -> begin let tac = let open Proofview.Notations in @@ -2149,7 +2114,7 @@ let known_state ?(redefine_qed=false) ~cache id = | Valid { proof } -> Proof_global.unfreeze proof; Proof_global.with_current_proof (fun _ p -> - feedback ~id:(State id) Feedback.AddedAxiom; + feedback ~id:id Feedback.AddedAxiom; fst (Pfedit.solve Vernacexpr.SelectAll None tac p), ()); Option.iter (fun expr -> stm_vernac_interp id { verbose = true; loc = Loc.ghost; expr; indentation = 0; @@ -2216,10 +2181,8 @@ let known_state ?(redefine_qed=false) ~cache id = (fun () -> resilient_tactic id cblock (fun () -> reach ~cache:`Shallow view.next; - Hooks.(call tactic_being_run true); Partac.vernac_interp ~solve ~abstract - cancel !Flags.async_proofs_n_tacworkers view.next id x; - Hooks.(call tactic_being_run false)) + cancel !Flags.async_proofs_n_tacworkers view.next id x) ), cache, true | `Cmd { cast = x; cqueue = `QueryQueue cancel } when Flags.async_proofs_is_master () -> (fun () -> @@ -2229,9 +2192,7 @@ let known_state ?(redefine_qed=false) ~cache id = | `Cmd { cast = x; ceff = eff; ctac = true; cblock } -> (fun () -> resilient_tactic id cblock (fun () -> reach view.next; - Hooks.(call tactic_being_run true); - stm_vernac_interp id x; - Hooks.(call tactic_being_run false)); + stm_vernac_interp id x); if eff then update_global_env () ), (if eff then `Yes else cache), true | `Cmd { cast = x; ceff = eff } -> (fun () -> @@ -2254,7 +2215,7 @@ let known_state ?(redefine_qed=false) ~cache id = with e when CErrors.noncritical e -> let (e, info) = CErrors.push e in let info = Stateid.add info ~valid:prev id in - iraise (e, info)); + Exninfo.iraise (e, info)); wall_clock_last_fork := Unix.gettimeofday () ), `Yes, true | `Qed ({ qast = x; keep; brinfo; brname } as qed, eop) -> @@ -2301,7 +2262,7 @@ let known_state ?(redefine_qed=false) ~cache id = if not delegate then ignore(Future.compute fp); reach view.next; stm_vernac_interp id ~proof x; - feedback ~id:(State id) Incomplete + feedback ~id:id Incomplete | { VCS.kind = `Master }, _ -> assert false end; Proof_global.discard_all () @@ -2337,9 +2298,9 @@ let known_state ?(redefine_qed=false) ~cache id = | `MaybeASync (start, pua, nodes, name, delegate) -> (fun () -> reach ~cache:`Shallow start; (* no sections *) - if List.is_empty (Environ.named_context (Global.env ())) - then pi1 (aux (`ASync (start, pua, nodes, name, delegate))) () - else pi1 (aux (`Sync (name, pua, `NoPU_NoHint_NoES))) () + if CList.is_empty (Environ.named_context (Global.env ())) + then Util.pi1 (aux (`ASync (start, pua, nodes, name, delegate))) () + else Util.pi1 (aux (`Sync (name, pua, `NoPU_NoHint_NoES))) () ), (if redefine_qed then `No else `Yes), true in aux (collect_proof keep (view.next, x) brname brinfo eop) @@ -2393,20 +2354,20 @@ let observe id = let e = CErrors.push e in VCS.print (); VCS.restore vcs; - iraise e + Exninfo.iraise e -let finish ?(print_goals=false) () = +let finish () = let head = VCS.current_branch () in observe (VCS.get_branch_pos head); - if print_goals then msg_notice (pr_open_cur_subgoals ()); VCS.print (); + (* EJGA: Setting here the proof state looks really wrong, and it + hides true bugs cf bug #5363. Also, what happens with observe? *) (* Some commands may by side effect change the proof mode *) match VCS.get_branch head with | { VCS.kind = `Edit (mode,_,_,_,_) } -> Proof_global.activate_proof_mode mode | { VCS.kind = `Proof (mode, _) } -> Proof_global.activate_proof_mode mode | _ -> () - let wait () = Slaves.wait_all_done (); VCS.print () @@ -2480,35 +2441,23 @@ let merge_proof_branch ~valid ?id qast keep brname = VCS.checkout VCS.Branch.master; `Unfocus qed_id | { VCS.kind = `Master } -> - iraise (State.exn_on ~valid Stateid.dummy (Proof_global.NoCurrentProof, Exninfo.null)) + Exninfo.iraise (State.exn_on ~valid Stateid.dummy (Proof_global.NoCurrentProof, Exninfo.null)) (* When tty is true, this code also does some of the job of the user interface: jump back to a state that is valid *) -let handle_failure (e, info) vcs tty = - if e = CErrors.Drop then iraise (e, info) else +let handle_failure (e, info) vcs = + if e = CErrors.Drop then Exninfo.iraise (e, info) else match Stateid.get info with | None -> VCS.restore vcs; VCS.print (); - if tty && interactive () = `Yes then begin - (* Hopefully the 1 to last state is valid *) - Backtrack.back_safe (); - VCS.checkout_shallowest_proof_branch (); - end; - VCS.print (); anomaly(str"error with no safe_id attached:" ++ spc() ++ CErrors.iprint_no_report (e, info)) | Some (safe_id, id) -> stm_prerr_endline (fun () -> "Failed at state " ^ Stateid.to_string id); VCS.restore vcs; - if tty && interactive () = `Yes then begin - (* We stay on a valid state *) - Backtrack.backto safe_id; - VCS.checkout_shallowest_proof_branch (); - Reach.known_state ~cache:(interactive ()) safe_id; - end; VCS.print (); - iraise (e, info) + Exninfo.iraise (e, info) let snapshot_vio ldir long_f_dot_vo = finish (); @@ -2520,7 +2469,7 @@ let snapshot_vio ldir long_f_dot_vo = let reset_task_queue = Slaves.reset_task_queue (* Document building *) -let process_transaction ?(newtip=Stateid.fresh ()) ~tty +let process_transaction ?(newtip=Stateid.fresh ()) ({ verbose; loc; expr } as x) c = stm_pperr_endline (fun () -> str "{{{ processing: " ++ pr_ast x); let vcs = VCS.backup () in @@ -2566,18 +2515,11 @@ let process_transaction ?(newtip=Stateid.fresh ()) ~tty anomaly(str"classifier: VtBack + VtLater must imply part_of_script") (* Query *) - | VtQuery (false,(report_id,route)), VtNow when tty = true -> - finish (); - (try Future.purify (stm_vernac_interp report_id ~route) - {x with verbose = true } - with e when CErrors.noncritical e -> - let e = CErrors.push e in - iraise (State.exn_on ~valid:Stateid.dummy report_id e)); `Ok | VtQuery (false,(report_id,route)), VtNow -> (try stm_vernac_interp report_id ~route x with e -> let e = CErrors.push e in - iraise (State.exn_on ~valid:Stateid.dummy report_id e)); `Ok + Exninfo.iraise (State.exn_on ~valid:Stateid.dummy report_id e)); `Ok | VtQuery (true,(report_id,_)), w -> assert(Stateid.equal report_id Stateid.dummy); let id = VCS.new_node ~id:newtip () in @@ -2704,7 +2646,7 @@ let process_transaction ?(newtip=Stateid.fresh ()) ~tty rc with e -> let e = CErrors.push e in - handle_failure e vcs tty + handle_failure e vcs let get_ast id = match VCS.visit id with @@ -2716,29 +2658,94 @@ let get_ast id = let stop_worker n = Slaves.cancel_worker n +(* We must parse on top of a state id, it should be something like: + + - get parsing information for that state. + - feed the parsable / parser with the right parsing information. + - call the parser + + Now, the invariant in ensured by the callers, but this is a bit + problematic. +*) +exception End_of_input + +let parse_sentence sid pa = + (* XXX: Should this restore the previous state? + Using reach here to try to really get to the + proper state makes the error resilience code fail *) + (* Reach.known_state ~cache:`Yes sid; *) + let cur_tip = VCS.cur_tip () in + let real_tip = !State.cur_id in + if not (Stateid.equal sid cur_tip) then + user_err ~hdr:"Stm.parse_sentence" + (str "Currently, the parsing api only supports parsing at the tip of the document." ++ fnl () ++ + str "You wanted to parse at: " ++ str (Stateid.to_string sid) ++ + str " but the current tip is: " ++ str (Stateid.to_string cur_tip)) ; + if not (Stateid.equal sid real_tip) && !Flags.debug && stm_debug then + Feedback.msg_debug + (str "Warning, the real tip doesn't match the current tip." ++ + str "You wanted to parse at: " ++ str (Stateid.to_string sid) ++ + str " but the real tip is: " ++ str (Stateid.to_string real_tip) ++ fnl () ++ + str "This is usually due to use of Stm.observe to evaluate a state different than the tip. " ++ + str "All is good if not parsing changes occur between the two states, however if they do, a problem might occur."); + Flags.with_option Flags.we_are_parsing (fun () -> + try + match Pcoq.Gram.entry_parse Pcoq.main_entry pa with + | None -> raise End_of_input + | Some com -> com + with e when CErrors.noncritical e -> + let (e, info) = CErrors.push e in + Exninfo.iraise (e, info)) + () + (* You may need to know the len + indentation of previous command to compute * the indentation of the current one. * Eg. foo. bar. * Here bar is indented of the indentation of foo + its strlen (4) *) -let ind_len_of id = - if Stateid.equal id Stateid.initial then 0 - else match (VCS.visit id).step with - | `Cmd { ctac = true; cast = { indentation; strlen } } -> - indentation + strlen - | _ -> 0 - -let add ~ontop ?newtip ?(check=ignore) verb eid s = +let ind_len_loc_of_id sid = + if Stateid.equal sid Stateid.initial then None + else match (VCS.visit sid).step with + | `Cmd { ctac = true; cast = { indentation; strlen; loc } } -> + Some (indentation, strlen, loc) + | _ -> None + +(* the indentation logic works like this: if the beginning of the + command is different than the start we are in a new line, thus + indentation follows from the starting position. Otherwise, we use + the previous one plus the offset. + + Note, this could maybe improved by handling more cases in + compute_indentation. *) + +let compute_indentation sid loc = + let open Loc in + (* The effective lenght is the lenght on the last line *) + let len = loc.ep - loc.bp in + let prev_indent = match ind_len_loc_of_id sid with + | None -> 0 + | Some (i,l,p) -> i + l + in + (* Now if the line has not changed, the need to add the previous indent *) + let eff_indent = loc.bp - loc.bol_pos in + if loc.bol_pos = 0 then + eff_indent + prev_indent, len + else + eff_indent, len + + +let add ~ontop ?newtip verb (loc, ast) = let cur_tip = VCS.cur_tip () in if not (Stateid.equal ontop cur_tip) then - (* For now, arbitrary edits should be announced with edit_at *) - anomaly(str"Not yet implemented, the GUI should not try this"); - let indentation, strlen, loc, ast = - vernac_parse ~indlen_prev:(fun () -> ind_len_of ontop) ?newtip eid s in + user_err ~hdr:"Stm.add" + (str "Stm.add called for a different state (" ++ str (Stateid.to_string ontop) ++ + str ") than the tip: " ++ str (Stateid.to_string cur_tip) ++ str "." ++ fnl () ++ + str "This is not supported yet, sorry."); + let indentation, strlen = compute_indentation ontop loc in CWarnings.set_current_loc loc; - check(loc,ast); + (* XXX: Classifiy vernac should be moved inside process transaction *) let clas = classify_vernac ast in let aast = { verbose = verb; indentation; strlen; loc; expr = ast } in - match process_transaction ?newtip ~tty:false aast clas with + match process_transaction ?newtip aast clas with | `Ok -> VCS.cur_tip (), `NewTip | `Unfocus qed_id -> qed_id, `Unfocus (VCS.cur_tip ()) @@ -2754,17 +2761,16 @@ let query ~at ?(report_with=(Stateid.dummy,default_route)) s = Future.purify (fun s -> if Stateid.equal at Stateid.dummy then finish () else Reach.known_state ~cache:`Yes at; - let newtip, route = report_with in - let indentation, strlen, loc, ast = vernac_parse ~newtip ~route 0 s in + let loc, ast = parse_sentence at s in + let indentation, strlen = compute_indentation at loc in CWarnings.set_current_loc loc; let clas = classify_vernac ast in let aast = { verbose = true; indentation; strlen; loc; expr = ast } in match clas with | VtStm (w,_), _ -> - ignore(process_transaction ~tty:false aast (VtStm (w,false), VtNow)) + ignore(process_transaction aast (VtStm (w,false), VtNow)) | _ -> - ignore(process_transaction - ~tty:false aast (VtQuery (false,report_with), VtNow))) + ignore(process_transaction aast (VtQuery (false,report_with), VtNow))) s let edit_at id = @@ -2888,37 +2894,16 @@ let edit_at id = stm_prerr_endline (fun () -> "Failed at state " ^ Stateid.to_string id); VCS.restore vcs; VCS.print (); - iraise (e, info) + Exninfo.iraise (e, info) let backup () = VCS.backup () let restore d = VCS.restore d +let get_current_state () = VCS.cur_tip () + (*********************** TTY API (PG, coqtop, coqc) ***************************) (******************************************************************************) -let interp verb (loc,e) = - let clas = classify_vernac e in - let aast = { verbose = verb; indentation = 0; strlen = 0; loc; expr = e } in - let rc = process_transaction ~tty:true aast clas in - if rc <> `Ok then anomaly(str"tty loop can't be mixed with the STM protocol"); - if interactive () = `Yes || - (!Flags.async_proofs_mode = Flags.APoff && - !Flags.compilation_mode = Flags.BuildVo) then - let vcs = VCS.backup () in - let print_goals = - verb && match clas with - | VtQuery _, _ -> false - | (VtProofStep _ | VtStm (VtBack _, _) | VtStartProof _), _ -> true - | _ -> not !Flags.coqtop_ui in - try finish ~print_goals () - with e -> - let e = CErrors.push e in - handle_failure e vcs true - -let finish () = finish () - -let get_current_state () = VCS.cur_tip () - let current_proof_depth () = let head = VCS.current_branch () in match VCS.get_branch head with @@ -2939,15 +2924,13 @@ let proofname b = match VCS.get_branch b with | _ -> None let get_all_proof_names () = - List.map unmangle (List.map_filter proofname (VCS.branches ())) + List.map unmangle (CList.map_filter proofname (VCS.branches ())) (* Export hooks *) let state_computed_hook = Hooks.state_computed_hook let state_ready_hook = Hooks.state_ready_hook -let parse_error_hook = Hooks.parse_error_hook let forward_feedback_hook = Hooks.forward_feedback_hook -let process_error_hook = Hooks.process_error_hook let unreachable_state_hook = Hooks.unreachable_state_hook let () = Hook.set Obligations.stm_get_fix_exn (fun () -> !State.fix_exn_ref) -let tactic_being_run_hook = Hooks.tactic_being_run_hook + (* vim:set foldmethod=marker: *) diff --git a/stm/stm.mli b/stm/stm.mli index 0f0a3c4e1..30e9629c6 100644 --- a/stm/stm.mli +++ b/stm/stm.mli @@ -13,24 +13,31 @@ open Loc (** state-transaction-machine interface *) -(* [add ontop check vebose eid s] adds a new command [s] on the state [ontop] - having edit id [eid]. [check] is called on the AST. - The [ontop] parameter is just for asserting the GUI is on sync, but - will eventually call edit_at on the fly if needed. - The sentence [s] is parsed in the state [ontop]. - If [newtip] is provided, then the returned state id is guaranteed to be - [newtip] *) -val add : - ontop:Stateid.t -> ?newtip:Stateid.t -> - ?check:(vernac_expr located -> unit) -> - bool -> edit_id -> string -> - Stateid.t * [ `NewTip | `Unfocus of Stateid.t ] - -(* parses and executes a command at a given state, throws away its side effects - but for the printings. Feedback is sent with report_with (defaults to dummy - state id) *) +(* [parse_sentence sid pa] Reads a sentence from [pa] with parsing + state [sid] Returns [End_of_input] if the stream ends *) +val parse_sentence : Stateid.t -> Pcoq.Gram.coq_parsable -> + Vernacexpr.vernac_expr Loc.located + +(* Reminder: A parsable [pa] is constructed using + [Pcoq.Gram.coq_parsable stream], where [stream : char Stream.t]. *) + +exception End_of_input + +(* [add ~ontop ?newtip verbose cmd] adds a new command [cmd] ontop of + the state [ontop]. + The [ontop] parameter just asserts that the GUI is on + sync, but it will eventually call edit_at on the fly if needed. + If [newtip] is provided, then the returned state id is guaranteed + to be [newtip] *) +val add : ontop:Stateid.t -> ?newtip:Stateid.t -> + bool -> Vernacexpr.vernac_expr Loc.located -> + Stateid.t * [ `NewTip | `Unfocus of Stateid.t ] + +(* [query at ?report_with cmd] Executes [cmd] at a given state [at], + throwing away side effects except messages. Feedback will + be sent with [report_with], which defaults to the dummy state id *) val query : - at:Stateid.t -> ?report_with:(Stateid.t * Feedback.route_id) -> string -> unit + at:Stateid.t -> ?report_with:(Stateid.t * Feedback.route_id) -> Pcoq.Gram.coq_parsable -> unit (* [edit_at id] is issued to change the editing zone. [`NewTip] is returned if the requested id is the new document tip hence the document portion following @@ -182,14 +189,10 @@ val register_proof_block_delimiter : * the name of the Task(s) above) *) val state_computed_hook : (Stateid.t -> in_cache:bool -> unit) Hook.t -val parse_error_hook : - (Feedback.edit_or_state_id -> Loc.t -> Pp.std_ppcmds -> unit) Hook.t val unreachable_state_hook : (Stateid.t -> Exninfo.iexn -> unit) Hook.t + (* ready means that master has it at hand *) val state_ready_hook : (Stateid.t -> unit) Hook.t -(* called with true before and with false after a tactic explicitly - * in the document is run *) -val tactic_being_run_hook : (bool -> unit) Hook.t (* Messages from the workers to the master *) val forward_feedback_hook : (Feedback.feedback -> unit) Hook.t @@ -202,16 +205,6 @@ type state = { val state_of_id : Stateid.t -> [ `Valid of state option | `Expired | `Error of exn ] -(** read-eval-print loop compatible interface ****************************** **) - -(* Adds a new line to the document. It replaces the core of Vernac.interp. - [finish] is called as the last bit of this function if the system - is running interactively (-emacs or coqtop). *) -val interp : bool -> vernac_expr located -> unit - (* Queries for backward compatibility *) val current_proof_depth : unit -> int val get_all_proof_names : unit -> Id.t list - -(* Hooks to be set by other Coq components in order to break file cycles *) -val process_error_hook : Future.fix_exn Hook.t diff --git a/toplevel/coqinit.ml b/toplevel/coqinit.ml index ffd0d7805..2b9a04dad 100644 --- a/toplevel/coqinit.ml +++ b/toplevel/coqinit.ml @@ -27,12 +27,12 @@ let set_rcfile s = rcfile := s; rcfile_specified := true let load_rc = ref true let no_load_rc () = load_rc := false -let load_rcfile() = +let load_rcfile sid = if !load_rc then try if !rcfile_specified then if CUnix.file_readable_p !rcfile then - Vernac.load_vernac false !rcfile + Vernac.load_vernac false sid !rcfile else raise (Sys_error ("Cannot read rcfile: "^ !rcfile)) else try @@ -43,8 +43,8 @@ let load_rcfile() = Envars.home ~warn / "."^rcdefaultname^"."^Coq_config.version; Envars.home ~warn / "."^rcdefaultname ] in - Vernac.load_vernac false inferedrc - with Not_found -> () + Vernac.load_vernac false sid inferedrc + with Not_found -> sid (* Flags.if_verbose mSGNL (str ("No coqrc or coqrc."^Coq_config.version^ @@ -55,7 +55,8 @@ let load_rcfile() = let () = Feedback.msg_info (str"Load of rcfile failed.") in iraise reraise else - Flags.if_verbose Feedback.msg_info (str"Skipping rcfile loading.") + (Flags.if_verbose Feedback.msg_info (str"Skipping rcfile loading."); + sid) (* Recursively puts dir in the LoadPath if -nois was not passed *) let add_stdlib_path ~unix_path ~coq_root ~with_ml = diff --git a/toplevel/coqinit.mli b/toplevel/coqinit.mli index 4ff87628c..3b42289ee 100644 --- a/toplevel/coqinit.mli +++ b/toplevel/coqinit.mli @@ -13,7 +13,7 @@ val set_debug : unit -> unit val set_rcfile : string -> unit val no_load_rc : unit -> unit -val load_rcfile : unit -> unit +val load_rcfile : Stateid.t -> Stateid.t val push_include : string -> Names.DirPath.t -> bool -> unit (** [push_include phys_path log_path implicit] *) diff --git a/toplevel/coqloop.ml b/toplevel/coqloop.ml index 2282932d4..4641a2bc8 100644 --- a/toplevel/coqloop.ml +++ b/toplevel/coqloop.ml @@ -7,11 +7,6 @@ (************************************************************************) open Pp -open CErrors -open Util -open Flags -open Vernac -open Pcoq let top_stderr x = Format.fprintf !Topfmt.err_ft "@[%a@]%!" pp_with x @@ -24,7 +19,7 @@ type input_buffer = { mutable str : Bytes.t; (* buffer of already read characters *) mutable len : int; (* number of chars in the buffer *) mutable bols : int list; (* offsets in str of beginning of lines *) - mutable tokens : Gram.coq_parsable; (* stream of tokens *) + mutable tokens : Pcoq.Gram.coq_parsable; (* stream of tokens *) mutable start : int } (* stream count of the first char of the buffer *) (* Double the size of the buffer. *) @@ -61,7 +56,7 @@ let prompt_char ic ibuf count = | ll::_ -> Int.equal ibuf.len ll | [] -> Int.equal ibuf.len 0 in - if bol && not !print_emacs then top_stderr (str (ibuf.prompt())); + if bol && not !Flags.print_emacs then top_stderr (str (ibuf.prompt())); try let c = input_char ic in if c == '\n' then ibuf.bols <- (ibuf.len+1) :: ibuf.bols; @@ -78,7 +73,7 @@ let reset_input_buffer ic ibuf = ibuf.str <- Bytes.empty; ibuf.len <- 0; ibuf.bols <- []; - ibuf.tokens <- Gram.parsable (Stream.from (prompt_char ic ibuf)); + ibuf.tokens <- Pcoq.Gram.parsable (Stream.from (prompt_char ic ibuf)); ibuf.start <- 0 (* Functions to print underlined locations from an input buffer. *) @@ -89,7 +84,7 @@ module TopErr = struct let get_bols_of_loc ibuf (bp,ep) = let add_line (b,e) lines = - if b < 0 || e < b then anomaly (Pp.str "Bad location"); + if b < 0 || e < b then CErrors.anomaly (Pp.str "Bad location"); match lines with | ([],None) -> ([], Some (b,e)) | (fl,oe) -> ((b,e)::fl, oe) @@ -174,7 +169,7 @@ let error_info_for_buffer ?loc buf = let fname = loc.Loc.fname in let hl, loc = (* We are in the toplevel *) - if String.equal fname "" then + if CString.equal fname "" then let nloc = adjust_loc_buf buf loc in if valid_buffer_loc buf loc then (fnl () ++ print_highlight_location buf nloc, nloc) @@ -223,7 +218,7 @@ let make_emacs_prompt() = let pending = Stm.get_all_proof_names() in let pendingprompt = List.fold_left - (fun acc x -> acc ^ (if String.is_empty acc then "" else "|") ^ Names.Id.to_string x) + (fun acc x -> acc ^ (if CString.is_empty acc then "" else "|") ^ Names.Id.to_string x) "" pending in let proof_info = if dpth >= 0 then string_of_int dpth else "0" in if !Flags.print_emacs then statnum ^ " |" ^ pendingprompt ^ "| " ^ proof_info ^ " < " @@ -243,7 +238,7 @@ let top_buffer = str = Bytes.empty; len = 0; bols = []; - tokens = Gram.parsable (Stream.of_list []); + tokens = Pcoq.Gram.parsable (Stream.of_list []); start = 0 } let set_prompt prompt = @@ -257,10 +252,10 @@ let set_prompt prompt = let parse_to_dot = let rec dot st = match Stream.next st with | Tok.KEYWORD ("."|"...") -> () - | Tok.EOI -> raise End_of_input + | Tok.EOI -> raise Stm.End_of_input | _ -> dot st in - Gram.Entry.of_parser "Coqtoplevel.dot" dot + Pcoq.Gram.Entry.of_parser "Coqtoplevel.dot" dot (* If an error occurred while parsing, we try to read the input until a dot token is encountered. @@ -268,21 +263,19 @@ let parse_to_dot = let rec discard_to_dot () = try - Gram.entry_parse parse_to_dot top_buffer.tokens + Pcoq.Gram.entry_parse parse_to_dot top_buffer.tokens with | Token.Error _ | CLexer.Error.E _ -> discard_to_dot () - | End_of_input -> raise End_of_input + | Stm.End_of_input -> raise Stm.End_of_input | e when CErrors.noncritical e -> () -let read_sentence input = - try - let (loc, _ as r) = Vernac.parse_sentence input in - CWarnings.set_current_loc loc; r +let read_sentence sid input = + try Stm.parse_sentence sid input with reraise -> let reraise = CErrors.push reraise in discard_to_dot (); TopErr.print_toplevel_parse_error reraise top_buffer; - iraise reraise + Exninfo.iraise reraise (** Coqloop Console feedback handler *) let coqloop_feed (fb : Feedback.feedback) = let open Feedback in @@ -312,25 +305,24 @@ let coqloop_feed (fb : Feedback.feedback) = let open Feedback in is caught and handled (i.e. not re-raised). *) -let do_vernac () = +let do_vernac sid = top_stderr (fnl()); - if !print_emacs then top_stderr (str (top_buffer.prompt())); + if !Flags.print_emacs then top_stderr (str (top_buffer.prompt())); resynch_buffer top_buffer; try let input = (top_buffer.tokens, None) in - Vernac.process_expr top_buffer.tokens (read_sentence input) + Vernac.process_expr sid top_buffer.tokens (read_sentence sid (fst input)) with - | End_of_input | CErrors.Quit -> + | Stm.End_of_input | CErrors.Quit -> top_stderr (fnl ()); raise CErrors.Quit | CErrors.Drop -> (* Last chance *) if Mltop.is_ocaml_top() then raise CErrors.Drop - else Feedback.msg_error (str "There is no ML toplevel.") + else (Feedback.msg_error (str "There is no ML toplevel."); sid) (* Exception printing is done now by the feedback listener. *) (* XXX: We need this hack due to the side effects of the exception printer and the reliance of Stm.define on attaching crutial state to exceptions *) - | any -> ignore (CErrors.(iprint (push any))) - + | any -> ignore (CErrors.(iprint (push any))); sid (** Main coq loop : read vernacular expressions until Drop is entered. Ctrl-C is handled internally as Sys.Break instead of aborting Coq. @@ -348,11 +340,16 @@ let loop_flush_all () = let rec loop () = Sys.catch_break true; - if !Flags.print_emacs then Vernacentries.qed_display_script := false; - Flags.coqtop_ui := true; try reset_input_buffer stdin top_buffer; - while true do do_vernac(); loop_flush_all () done + (* Be careful to keep this loop tail-recursive *) + let rec vernac_loop sid = + let nsid = do_vernac sid in + loop_flush_all (); + vernac_loop nsid + (* We recover the current stateid, threading from the caller is + not possible due exceptions. *) + in vernac_loop (Stm.get_current_state ()) with | CErrors.Drop -> () | CErrors.Quit -> exit 0 diff --git a/toplevel/coqloop.mli b/toplevel/coqloop.mli index 8a34ded6d..66bbf43f6 100644 --- a/toplevel/coqloop.mli +++ b/toplevel/coqloop.mli @@ -31,7 +31,7 @@ val coqloop_feed : Feedback.feedback -> unit (** Parse and execute one vernac command. *) -val do_vernac : unit -> unit +val do_vernac : Stateid.t -> Stateid.t (** Main entry point of Coq: read and execute vernac commands. *) diff --git a/toplevel/coqtop.ml b/toplevel/coqtop.ml index aca94e63d..c259beb17 100644 --- a/toplevel/coqtop.ml +++ b/toplevel/coqtop.ml @@ -8,11 +8,8 @@ open Pp open CErrors -open Util open Flags -open Names open Libnames -open States open Coqinit let () = at_exit flush_all @@ -133,10 +130,10 @@ let engage () = let set_batch_mode () = batch_mode := true -let toplevel_default_name = DirPath.make [Id.of_string "Top"] +let toplevel_default_name = Names.(DirPath.make [Id.of_string "Top"]) let toplevel_name = ref toplevel_default_name let set_toplevel_name dir = - if DirPath.is_empty dir then error "Need a non empty toplevel module name"; + if Names.DirPath.is_empty dir then error "Need a non empty toplevel module name"; toplevel_name := dir let remove_top_ml () = Mltop.remove () @@ -150,9 +147,9 @@ let set_inputstate s = warn_deprecated_inputstate (); inputstate:=s let inputstate () = - if not (String.is_empty !inputstate) then + if not (CString.is_empty !inputstate) then let fname = Loadpath.locate_file (CUnix.make_suffix !inputstate ".coq") in - intern_state fname + States.intern_state fname let warn_deprecated_outputstate = CWarnings.create ~name:"deprecated-outputstate" ~category:"deprecated" @@ -164,9 +161,9 @@ let set_outputstate s = warn_deprecated_outputstate (); outputstate:=s let outputstate () = - if not (String.is_empty !outputstate) then + if not (CString.is_empty !outputstate) then let fname = CUnix.make_suffix !outputstate ".coq" in - extern_state fname + States.extern_state fname let set_include d p implicit = let p = dirpath_of_string p in @@ -175,15 +172,15 @@ let set_include d p implicit = let load_vernacular_list = ref ([] : (string * bool) list) let add_load_vernacular verb s = load_vernacular_list := ((CUnix.make_suffix s ".v"),verb) :: !load_vernacular_list -let load_vernacular () = - List.iter - (fun (s,b) -> +let load_vernacular sid = + List.fold_left + (fun sid (s,v) -> let s = Loadpath.locate_file s in if !Flags.beautify then - with_option beautify_file (Vernac.load_vernac b) s + with_option beautify_file (Vernac.load_vernac v sid) s else - Vernac.load_vernac b s) - (List.rev !load_vernacular_list) + Vernac.load_vernac v sid s) + sid (List.rev !load_vernacular_list) let load_vernacular_obj = ref ([] : string list) let add_vernac_obj s = load_vernacular_obj := s :: !load_vernacular_obj @@ -250,7 +247,6 @@ let set_emacs () = if not (Option.is_empty !toploop) then error "Flag -emacs is incompatible with a custom toplevel loop"; Flags.print_emacs := true; - Vernacentries.qed_display_script := false; color := `OFF (** Options for CoqIDE *) @@ -380,7 +376,7 @@ let get_host_port opt s = let get_error_resilience opt = function | "on" | "all" | "yes" -> `All | "off" | "no" -> `None - | s -> `Only (String.split ',' s) + | s -> `Only (CString.split ',' s) let get_task_list s = List.map int_of_string (Str.split (Str.regexp ",") s) @@ -625,7 +621,7 @@ let init_toplevel arglist = init_load_path (); Option.iter Mltop.load_ml_object_raw !toploop; let extras = !toploop_init extras in - if not (List.is_empty extras) then begin + if not (CList.is_empty extras) then begin prerr_endline ("Don't know what to do with "^String.concat " " extras); prerr_endline "See -help for the list of supported options"; exit 1 @@ -634,15 +630,16 @@ let init_toplevel arglist = inputstate (); Mltop.init_known_plugins (); engage (); - if (not !batch_mode || List.is_empty !compile_list) + if (not !batch_mode || CList.is_empty !compile_list) && Global.env_is_initial () then Declaremods.start_library !toplevel_name; init_library_roots (); load_vernac_obj (); require (); Stm.init (); - load_rcfile(); - load_vernacular (); + let sid = load_rcfile (Stm.get_current_state ()) in + (* XXX: We ignore this for now, but should be threaded to the toplevels *) + let _sid = load_vernacular sid in compile_files (); schedule_vio_checking (); schedule_vio_compilation (); diff --git a/toplevel/vernac.ml b/toplevel/vernac.ml index 9917a49b4..98458a57a 100644 --- a/toplevel/vernac.ml +++ b/toplevel/vernac.ml @@ -13,111 +13,30 @@ open CErrors open Util open Flags open Vernacexpr +open Vernacprop (* 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). *) -let user_error loc s = CErrors.user_err ~loc (str s) - -(* Navigation commands are allowed in a coqtop session but not in a .v file *) - -let rec is_navigation_vernac = function - | VernacResetInitial - | VernacResetName _ - | VernacBacktrack _ - | VernacBackTo _ - | VernacBack _ - | VernacStm _ -> true - | VernacRedirect (_, (_,c)) - | VernacTime (_,c) -> - is_navigation_vernac c (* Time Back* is harmless *) - | c -> is_deep_navigation_vernac c - -and is_deep_navigation_vernac = function - | VernacTimeout (_,c) | VernacFail c -> is_navigation_vernac c - | _ -> false - -(* NB: Reset is now allowed again as asked by A. Chlipala *) - -let is_reset = function - | VernacResetInitial | VernacResetName _ -> true - | _ -> false - -let checknav_simple loc cmd = +let checknav_simple (loc, cmd) = if is_navigation_vernac cmd && not (is_reset cmd) then - user_error loc "Navigation commands forbidden in files." + CErrors.user_err ~loc (str "Navigation commands forbidden in files.") -let checknav_deep loc ast = +let checknav_deep (loc, ast) = if is_deep_navigation_vernac ast then - user_error loc "Navigation commands forbidden in nested commands." - -(* When doing Load on a file, two behaviors are possible: - - - either the history stack is grown by only one command, - the "Load" itself. This is mandatory for command-counting - interfaces (CoqIDE). - - - either each individual sub-commands in the file is added - to the history stack. This allows commands like Show Script - to work across the loaded file boundary (cf. bug #2820). - - The best of the two could probably be combined someday, - in the meanwhile we use a flag. *) - -let atomic_load = ref true - -let _ = - Goptions.declare_bool_option - { Goptions.optsync = true; - Goptions.optdepr = false; - Goptions.optname = "atomic registration of commands in a Load"; - Goptions.optkey = ["Atomic";"Load"]; - Goptions.optread = (fun () -> !atomic_load); - Goptions.optwrite = ((:=) atomic_load) } + CErrors.user_err ~loc (str "Navigation commands forbidden in nested commands.") let disable_drop = function | Drop -> CErrors.error "Drop is forbidden." | e -> e -(* Opening and closing a channel. Open it twice when verbose: the first - channel is used to read the commands, and the second one to print them. - Note: we could use only one thanks to seek_in, but seeking on and on in - the file we parse seems a bit risky to me. B.B. *) - -let open_file_twice_if verbosely longfname = - let in_chan = open_utf8_file_in longfname in - let verb_ch = - if verbosely then Some (open_utf8_file_in longfname) else None in - let po = Pcoq.Gram.parsable ~file:longfname (Stream.of_channel in_chan) in - (in_chan, longfname, (po, verb_ch)) - -let close_input in_chan (_,verb) = - try - close_in in_chan; - match verb with - | Some verb_ch -> close_in verb_ch - | _ -> () - with e when CErrors.noncritical e -> () - -let verbose_phrase verbch loc = - let loc = Loc.unloc loc in - match verbch with - | Some ch -> - let len = snd loc - fst loc in - let s = Bytes.create len in - seek_in ch (fst loc); - really_input ch s 0 len; - Feedback.msg_notice (str (Bytes.to_string s)) - | None -> () - -exception End_of_input - -let parse_sentence = Flags.with_option Flags.we_are_parsing - (fun (po, verbch) -> - match Pcoq.Gram.entry_parse Pcoq.main_entry po with - | Some (loc,_ as com) -> verbose_phrase verbch loc; com - | None -> raise End_of_input) +(* Echo from a buffer based on position. + XXX: Should move to utility file. *) +let vernac_echo loc in_chan = let open Loc in + let len = loc.ep - loc.bp in + seek_in in_chan loc.bp; + Feedback.msg_notice @@ str @@ really_input_string in_chan len (* vernac parses the given stream, executes interpfun on the syntax tree it * parses, and is verbose on "primitives" commands if verbosely is true *) @@ -184,20 +103,43 @@ let print_cmd_header loc com = Pp.pp_with !Topfmt.std_ft (pp_cmd_header loc com); Format.pp_print_flush !Topfmt.std_ft () -let rec interp_vernac po chan_beautify checknav (loc,com) = +let pr_open_cur_subgoals () = + try Printer.pr_open_subgoals () + with Proof_global.NoCurrentProof -> Pp.str "" + +let rec interp_vernac sid po (loc,com) = let interp = function | VernacLoad (verbosely, fname) -> let fname = Envars.expand_path_macros ~warn:(fun x -> Feedback.msg_warning (str x)) fname in let fname = CUnix.make_suffix fname ".v" in let f = Loadpath.locate_file fname in - load_vernac verbosely f - - | v -> Stm.interp (Flags.is_verbose()) (loc,v) + load_vernac verbosely sid f + | v -> + try + let nsid, ntip = Stm.add sid (Flags.is_verbose()) (loc,v) in + + (* Main STM interaction *) + if ntip <> `NewTip then + anomaly (str "vernac.ml: We got an unfocus operation on the toplevel!"); + (* Due to bug #5363 we cannot use observe here as we should, + it otherwise reveals bugs *) + (* Stm.observe nsid; *) + Stm.finish (); + + (* We could use a more refined criteria depending on the + vernac. For now we imitate the old approach. *) + let print_goals = not (!Flags.batch_mode || is_query v) in + + if print_goals then Feedback.msg_notice (pr_open_cur_subgoals ()); + nsid + + with exn when CErrors.noncritical exn -> + ignore(Stm.edit_at sid); + raise exn in try - checknav loc com; - if !beautify then pr_new_syntax po chan_beautify loc (Some com); - (* XXX: This is not 100% correct if called from an IDE context *) + (* The -time option is only supported from console-based + clients due to the way it prints. *) if !Flags.time then print_cmd_header loc com; let com = if !Flags.time then VernacTime (loc,com) else com in interp com @@ -208,26 +150,39 @@ let rec interp_vernac po chan_beautify checknav (loc,com) = else iraise (reraise, info) (* Load a vernac file. CErrors are annotated with file and location *) -and load_vernac verbosely file = +and load_vernac verbosely sid file = let chan_beautify = if !Flags.beautify_file then open_out (file^beautify_suffix) else stdout in - let (in_chan, fname, input) = open_file_twice_if verbosely file in + let in_chan = open_utf8_file_in file in + let in_echo = if verbosely then Some (open_utf8_file_in file) else None in + let in_pa = Pcoq.Gram.parsable ~file (Stream.of_channel in_chan) in + let rsid = ref sid in try (* we go out of the following infinite loop when a End_of_input is * raised, which means that we raised the end of the file being loaded *) while true do - let loc_ast = Flags.silently parse_sentence input in - CWarnings.set_current_loc (fst loc_ast); - Flags.silently (interp_vernac (fst input) chan_beautify checknav_simple) loc_ast; - done + let loc, ast = Stm.parse_sentence !rsid in_pa in + + (* Printing of vernacs *) + if !beautify then pr_new_syntax in_pa chan_beautify loc (Some ast); + Option.iter (vernac_echo loc) in_echo; + + checknav_simple (loc, ast); + let nsid = Flags.silently (interp_vernac !rsid in_pa) (loc, ast) in + rsid := nsid + done; + !rsid with any -> (* whatever the exception *) let (e, info) = CErrors.push any in - close_input in_chan input; (* we must close the file first *) + close_in in_chan; + Option.iter close_in in_echo; match e with - | End_of_input -> + | Stm.End_of_input -> + (* Is this called so comments at EOF are printed? *) if !beautify then - pr_new_syntax (fst input) chan_beautify (Loc.make_loc (max_int,max_int)) None; + pr_new_syntax in_pa chan_beautify (Loc.make_loc (max_int,max_int)) None; if !Flags.beautify_file then close_out chan_beautify; + !rsid | reraise -> if !Flags.beautify_file then close_out chan_beautify; iraise (disable_drop e, info) @@ -239,7 +194,9 @@ and load_vernac verbosely file = of a new state label). An example of state-preserving command is one coming from the query panel of Coqide. *) -let process_expr po loc_ast = interp_vernac po stdout checknav_deep loc_ast +let process_expr sid po loc_ast = + checknav_deep loc_ast; + interp_vernac sid po loc_ast (* XML output hooks *) let (f_xml_start_library, xml_start_library) = Hook.make ~default:ignore () @@ -308,7 +265,7 @@ let compile verbosely f = Dumpglob.dump_string ("F" ^ Names.DirPath.to_string ldir ^ "\n"); if !Flags.xml_export then Hook.get f_xml_start_library (); let wall_clock1 = Unix.gettimeofday () in - let _ = load_vernac verbosely long_f_dot_v in + let _ = load_vernac verbosely (Stm.get_current_state ()) long_f_dot_v in Stm.join (); let wall_clock2 = Unix.gettimeofday () in check_pending_proofs (); @@ -328,7 +285,7 @@ let compile verbosely f = let ldir = Flags.verbosely Library.start_library long_f_dot_vio in Dumpglob.noglob (); Stm.set_compilation_hints long_f_dot_vio; - let _ = load_vernac verbosely long_f_dot_v in + let _ = load_vernac verbosely (Stm.get_current_state ()) long_f_dot_v in Stm.finish (); check_pending_proofs (); Stm.snapshot_vio ldir long_f_dot_vio; @@ -347,6 +304,3 @@ let compile v f = ignore(CoqworkmgrApi.get 1); compile v f; CoqworkmgrApi.giveback 1 - -let () = Hook.set Stm.process_error_hook - ExplainErr.process_vernac_interp_error diff --git a/toplevel/vernac.mli b/toplevel/vernac.mli index 0d9f5871a..e75f8f9e8 100644 --- a/toplevel/vernac.mli +++ b/toplevel/vernac.mli @@ -8,30 +8,16 @@ (** Parsing of vernacular. *) -(** Read a vernac command on the specified input (parse only). - Raises [End_of_file] if EOF (or Ctrl-D) is reached. *) - -val parse_sentence : Pcoq.Gram.coq_parsable * in_channel option -> - Loc.t * Vernacexpr.vernac_expr - (** Reads and executes vernac commands from a stream. *) +val process_expr : Stateid.t -> Pcoq.Gram.coq_parsable -> Vernacexpr.vernac_expr Loc.located -> Stateid.t -exception End_of_input +(** [load_vernac echo sid file] Loads [file] on top of [sid], will + echo the commands if [echo] is set. *) +val load_vernac : bool -> Stateid.t -> string -> Stateid.t -val process_expr : Pcoq.Gram.coq_parsable -> Loc.t * Vernacexpr.vernac_expr -> unit +(** Compile a vernac file, (f is assumed without .v suffix) *) +val compile : bool -> string -> unit (** Set XML hooks *) val xml_start_library : (unit -> unit) Hook.t val xml_end_library : (unit -> unit) Hook.t - -(** Load a vernac file, verbosely or not. Errors are annotated with file - and location *) - -val load_vernac : bool -> string -> unit - - -(** Compile a vernac file, verbosely or not (f is assumed without .v suffix) *) - -val compile : bool -> string -> unit - -val is_navigation_vernac : Vernacexpr.vernac_expr -> bool diff --git a/vernac/vernac.mllib b/vernac/vernac.mllib index 283c095eb..d631fae8a 100644 --- a/vernac/vernac.mllib +++ b/vernac/vernac.mllib @@ -1,3 +1,4 @@ +Vernacprop Lemmas Himsg ExplainErr diff --git a/vernac/vernacentries.ml b/vernac/vernacentries.ml index 53d49ddbc..287584d56 100644 --- a/vernac/vernacentries.ml +++ b/vernac/vernacentries.ml @@ -84,24 +84,10 @@ let show_universes () = Feedback.msg_notice (Termops.pr_evar_universe_context (Evd.evar_universe_context sigma)); Feedback.msg_notice (str"Normalized constraints: " ++ Univ.pr_universe_context_set (Termops.pr_evd_level sigma) ctx) -let show_prooftree () = - (* Spiwack: proof tree is currently not working *) - () - -let enable_goal_printing = ref true - -let print_subgoals () = - if !enable_goal_printing && is_verbose () - then begin - Feedback.msg_notice (pr_open_subgoals ()) - end - -let try_print_subgoals () = - try print_subgoals () with Proof_global.NoCurrentProof | UserError _ -> () - - - (* Simulate the Intro(s) tactic *) +(* Spiwack: proof tree is currently not working *) +let show_prooftree () = () +(* Simulate the Intro(s) tactic *) let show_intro all = let open EConstr in let pf = get_pftreestate() in @@ -513,8 +499,6 @@ let vernac_start_proof locality p kind l lettop = (str "Let declarations can only be used in proof editing mode."); start_proof_and_print (local, p, Proof kind) l no_hook -let qed_display_script = ref true - let vernac_end_proof ?proof = function | Admitted -> save_proof ?proof Admitted | Proved (_,_) as e -> save_proof ?proof e @@ -1381,15 +1365,6 @@ let _ = declare_bool_option { optsync = true; optdepr = false; - optname = "record printing"; - optkey = ["Printing";"Records"]; - optread = (fun () -> !Flags.record_print); - optwrite = (fun b -> Flags.record_print := b) } - -let _ = - declare_bool_option - { optsync = true; - optdepr = false; optname = "use of the program extension"; optkey = ["Program";"Mode"]; optread = (fun () -> !Flags.program_mode); @@ -1438,15 +1413,6 @@ let _ = let _ = declare_int_option - { optsync = false; - optdepr = false; - optname = "the hypotheses limit"; - optkey = ["Hyps";"Limit"]; - optread = Flags.print_hyps_limit; - optwrite = Flags.set_print_hyps_limit } - -let _ = - declare_int_option { optsync = true; optdepr = false; optname = "the printing depth"; diff --git a/vernac/vernacentries.mli b/vernac/vernacentries.mli index 7cdc8dd06..fb2899515 100644 --- a/vernac/vernacentries.mli +++ b/vernac/vernacentries.mli @@ -11,42 +11,15 @@ open Misctypes val dump_global : Libnames.reference or_by_notation -> unit (** Vernacular entries *) - -val show_prooftree : unit -> unit - -val show_node : unit -> unit - val vernac_require : Libnames.reference option -> bool option -> Libnames.reference list -> unit -(** This function can be used by any command that want to observe terms - in the context of the current goal *) -val get_current_context_of_args : int option -> Evd.evar_map * Environ.env - (** The main interpretation function of vernacular expressions *) -val interp : +val interp : ?verbosely:bool -> ?proof:Proof_global.closed_proof -> Loc.t * Vernacexpr.vernac_expr -> unit -(** Print subgoals when the verbose flag is on. - Meant to be used inside vernac commands from plugins. *) - -val print_subgoals : unit -> unit -val try_print_subgoals : unit -> unit - -(** The printing of goals via [print_subgoals] or during - [interp] can be controlled by the following flag. - Used for instance by coqide, since it has its own - goal-fetching mechanism. *) - -val enable_goal_printing : bool ref - -(** Should Qed try to display the proof script ? - True by default, but false in ProofGeneral and coqIDE *) - -val qed_display_script : bool ref - (** Prepare a "match" template for a given inductive type. For each branch of the match, we list the constructor name followed by enough pattern variables. @@ -55,9 +28,6 @@ val qed_display_script : bool ref val make_cases : string -> string list list -val vernac_end_proof : - ?proof:Proof_global.closed_proof -> Vernacexpr.proof_end -> unit - val with_fail : bool -> (unit -> unit) -> unit val command_focus : unit Proof.focus_kind diff --git a/vernac/vernacprop.ml b/vernac/vernacprop.ml new file mode 100644 index 000000000..ec78d6012 --- /dev/null +++ b/vernac/vernacprop.ml @@ -0,0 +1,53 @@ +(************************************************************************) +(* v * The Coq Proof Assistant / The Coq Development Team *) +(* <O___,, * INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2016 *) +(* \VV/ **************************************************************) +(* // * This file is distributed under the terms of the *) +(* * GNU Lesser General Public License Version 2.1 *) +(************************************************************************) + +(* We define some high-level properties of vernacular commands, used + mainly by the UI components *) + +open Vernacexpr + +(* Navigation commands are allowed in a coqtop session but not in a .v file *) +let rec is_navigation_vernac = function + | VernacResetInitial + | VernacResetName _ + | VernacBacktrack _ + | VernacBackTo _ + | VernacBack _ + | VernacStm _ -> true + | VernacRedirect (_, (_,c)) + | VernacTime (_,c) -> + is_navigation_vernac c (* Time Back* is harmless *) + | c -> is_deep_navigation_vernac c + +and is_deep_navigation_vernac = function + | VernacTimeout (_,c) | VernacFail c -> is_navigation_vernac c + | _ -> false + +(* NB: Reset is now allowed again as asked by A. Chlipala *) +let is_reset = function + | VernacResetInitial | VernacResetName _ -> true + | _ -> false + +let is_debug cmd = match cmd with + | VernacSetOption (["Ltac";"Debug"], _) -> true + | _ -> false + +let is_query cmd = match cmd with + | VernacChdir None + | VernacMemOption _ + | VernacPrintOption _ + | VernacCheckMayEval _ + | VernacGlobalCheck _ + | VernacPrint _ + | VernacSearch _ + | VernacLocate _ -> true + | _ -> false + +let is_undo cmd = match cmd with + | VernacUndo _ | VernacUndoTo _ -> true + | _ -> false diff --git a/vernac/vernacprop.mli b/vernac/vernacprop.mli new file mode 100644 index 000000000..c235ecfb5 --- /dev/null +++ b/vernac/vernacprop.mli @@ -0,0 +1,19 @@ +(************************************************************************) +(* v * The Coq Proof Assistant / The Coq Development Team *) +(* <O___,, * INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2016 *) +(* \VV/ **************************************************************) +(* // * This file is distributed under the terms of the *) +(* * GNU Lesser General Public License Version 2.1 *) +(************************************************************************) + +(* We define some high-level properties of vernacular commands, used + mainly by the UI components *) + +open Vernacexpr + +val is_navigation_vernac : vernac_expr -> bool +val is_deep_navigation_vernac : vernac_expr -> bool +val is_reset : vernac_expr -> bool +val is_query : vernac_expr -> bool +val is_debug : vernac_expr -> bool +val is_undo : vernac_expr -> bool |