diff options
| author | gareuselesinge | 2013-09-30 16:09:57 +0000 |
|---|---|---|
| committer | gareuselesinge | 2013-09-30 16:09:57 +0000 |
| commit | 15102cce6941d19c7d630fd9c42e12aa676e7a08 (patch) | |
| tree | 4d0b5ef7f4de4ee77a21e235882ff7e14330969a | |
| parent | 698a1ca948794ae9509547ddabd57b5f35512f03 (diff) | |
STM: some refactoring, support revised CoqIDE protocol
git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/coq/trunk@16815 85f007b7-540e-0410-9357-904b9bb8a0f7
| -rw-r--r-- | toplevel/stm.ml | 796 | ||||
| -rw-r--r-- | toplevel/stm.mli | 52 | ||||
| -rw-r--r-- | toplevel/toplevel.ml | 12 | ||||
| -rw-r--r-- | toplevel/vernac.ml | 2 |
4 files changed, 574 insertions, 288 deletions
diff --git a/toplevel/stm.ml b/toplevel/stm.ml index 4e2c8577cf..4a1ce901ca 100644 --- a/toplevel/stm.ml +++ b/toplevel/stm.ml @@ -20,8 +20,8 @@ open Vernac_classifier let interactive () = !Flags.ide_slave || !Flags.print_emacs || not !Flags.batch_mode -(* Wrap interp to set the feedback id *) -let interp ?proof id (verbosely, loc, expr) = +(* Wrapper for Vernacentries.interp to set the feedback id *) +let vernac_interp ?proof id (verbosely, loc, expr) = let internal_command = function | VernacResetName _ | VernacResetInitial | VernacBack _ | VernacBackTo _ | VernacRestart | VernacUndo _ | VernacUndoTo _ @@ -35,16 +35,36 @@ let interp ?proof id (verbosely, loc, expr) = with e -> raise (Errors.push (Cerrors.process_vernac_interp_error e)) end +(* Wrapper for Vernac.parse_sentence to set the feedback id *) +let vernac_parse eid s = + set_id_for_feedback (Interface.Edit eid); + 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) + () + type ast = bool * Loc.t * vernac_expr let pr_ast (_, _, v) = pr_vernac v module Vcs_ = Vcs.Make(Stateid) - -type branch_type = [ `Master | `Proof of string * int ] +type future_proof = Entries.proof_output list Future.computation +type proof_mode = string +type depth = int +type branch_type = + [ `Master + | `Proof of proof_mode * depth + | `Edit of proof_mode * Stateid.t * Stateid.t ] type cmd_t = ast * Id.t list type fork_t = ast * Vcs_.Branch.t * Id.t list -type qed_t = - ast * vernac_qed_type * (Vcs_.Branch.t * branch_type Vcs_.branch_info) +type qed_t = { + qast : ast; + keep : vernac_qed_type; + mutable fproof : future_proof option; + brname : Vcs_.Branch.t; + brinfo : branch_type Vcs_.branch_info +} type seff_t = ast option type alias_t = Stateid.t type transaction = @@ -58,18 +78,25 @@ type step = [ `Cmd of cmd_t | `Fork of fork_t | `Qed of qed_t * Stateid.t - (* TODO : is the state id carried by `Ast relevant? *) | `Sideff of [ `Ast of ast * Stateid.t | `Id of Stateid.t ] | `Alias of alias_t ] type visit = { step : step; next : Stateid.t } -type state = States.state * Proof_global.state -type state_info = { (* Make private *) +type state = { + system : States.state; + proof : Proof_global.state; + shallow : bool +} +type branch = Vcs_.Branch.t * branch_type Vcs_.branch_info +type backup = { mine : branch; others : branch list } +type 'vcs state_info = { (* Make private *) mutable n_reached : int; mutable n_goals : int; mutable state : state option; + mutable vcs_backup : 'vcs option * backup option; } -let default_info () = { n_reached = 0; n_goals = 0; state = None } +let default_info () = + { n_reached = 0; n_goals = 0; state = None; vcs_backup = None,None } (* Functions that work on a Vcs with a specific branch type *) module Vcs_aux : sig @@ -78,27 +105,57 @@ module Vcs_aux : sig val find_proof_at_depth : (branch_type, 't, 'i) Vcs_.t -> int -> Vcs_.Branch.t * branch_type Vcs_.branch_info + exception Expired + val visit : (branch_type, transaction,'i) Vcs_.t -> Vcs_.Dag.node -> visit end = struct (* {{{ *) let proof_nesting vcs = List.fold_left max 0 (List.map_filter - (function { Vcs_.kind = `Proof (_,n) } -> Some n | _ -> None) + (function + | { Vcs_.kind = `Proof (_,n) } -> Some n + | { Vcs_.kind = `Edit _ } -> Some 1 + | _ -> None) (List.map (Vcs_.get_branch vcs) (Vcs_.branches vcs))) let find_proof_at_depth vcs pl = try List.find (function | _, { Vcs_.kind = `Proof(m, n) } -> Int.equal n pl + | _, { Vcs_.kind = `Edit _ } -> anomaly(str"find_proof_at_depth") | _ -> false) (List.map (fun h -> h, Vcs_.get_branch vcs h) (Vcs_.branches vcs)) with Not_found -> failwith "find_proof_at_depth" + exception Expired + let visit vcs id = + if Stateid.equal id Stateid.initial then + anomaly(str"Visiting the initial state id") + else if Stateid.equal id Stateid.dummy then + anomaly(str"Visiting the dummy state id") + else + try + match Vcs_.Dag.from_node (Vcs_.dag vcs) id with + | [n, Cmd x] -> { step = `Cmd x; next = n } + | [n, Alias x] -> { step = `Alias x; next = n } + | [n, Fork x] -> { step = `Fork x; next = n } + | [n, Qed x; p, Noop] + | [p, Noop; n, Qed x] -> { step = `Qed (x,p); next = n } + | [n, Sideff None; p, Noop] + | [p, Noop; n, Sideff None]-> { step = `Sideff (`Id p); next = n } + | [n, Sideff (Some x); p, Noop] + | [p, Noop; n, Sideff (Some x)]-> { step = `Sideff(`Ast (x,p)); next = n } + | [n, Sideff (Some x)]-> {step = `Sideff(`Ast (x,Stateid.dummy)); next=n} + | _ -> anomaly (str ("Malformed VCS at node "^Stateid.to_string id)) + with Not_found -> raise Expired + end (* }}} *) (* Imperative wrap around VCS to obtain _the_ VCS *) module VCS : sig + exception Expired + module Branch : (module type of Vcs_.Branch with type t = Vcs_.Branch.t) type id = Stateid.t type 'branch_type branch_info = 'branch_type Vcs_.branch_info = { @@ -107,8 +164,7 @@ module VCS : sig pos : id; } - type vcs = (branch_type, transaction, state_info) Vcs_.t - exception Expired + type vcs = (branch_type, transaction, vcs state_info) Vcs_.t val init : id -> unit @@ -119,12 +175,17 @@ module VCS : sig val get_branch_pos : Branch.t -> id val new_node : unit -> id val merge : id -> ours:transaction -> ?into:Branch.t -> Branch.t -> unit + val rewrite_merge : id -> ours:transaction -> at:id -> Branch.t -> unit val delete_branch : Branch.t -> unit val commit : id -> transaction -> unit val mk_branch_name : ast -> Branch.t - val branch : Branch.t -> branch_type -> unit - - val get_info : id -> state_info + val edit_branch : Branch.t + val branch : ?root:id -> ?pos:id -> Branch.t -> branch_type -> unit + val reset_branch : Branch.t -> id -> unit + val reachable : id -> Vcs_.NodeSet.t + val cur_tip : unit -> id + + val get_info : id -> vcs state_info val reached : id -> bool -> unit val goals : id -> int -> unit val set_state : id -> state -> unit @@ -134,12 +195,16 @@ module VCS : sig (* cuts from start -> stop, raising Expired if some nodes are not there *) val slice : start:id -> stop:id -> purify:(state -> state) -> vcs - val create_cluster : id list -> unit + val create_cluster : id list -> tip:id -> unit + val cluster_of : id -> id option + val delete_cluster_of : id -> unit val proof_nesting : unit -> int val checkout_shallowest_proof_branch : unit -> unit val propagate_sideff : ast option -> unit + val gc : unit -> unit + val visit : id -> visit val print : unit -> unit @@ -150,6 +215,7 @@ module VCS : sig end = struct (* {{{ *) include Vcs_ + exception Expired = Vcs_aux.Expired module StateidSet = Set.Make(Stateid) open Printf @@ -157,7 +223,7 @@ end = struct (* {{{ *) let print_dag vcs () = (* {{{ *) let fname = "stm" ^ string_of_int (max 0 !Flags.coq_slave_mode) in let string_of_transaction = function - | Cmd (t, _) | Fork (t, _, _) -> + | Cmd (t, _) | Fork (t, _,_) -> (try string_of_ppcmds (pr_ast t) with _ -> "ERR") | Sideff (Some t) -> sprintf "Sideff(%s)" @@ -165,7 +231,7 @@ end = struct (* {{{ *) | Sideff None -> "EnvChange" | Noop -> " " | Alias id -> sprintf "Alias(%s)" (Stateid.to_string id) - | Qed (a,_,_) -> string_of_ppcmds (pr_ast a) in + | Qed { qast } -> string_of_ppcmds (pr_ast qast) in let is_green id = match get_info vcs id with | Some { state = Some _ } -> true @@ -239,8 +305,7 @@ end = struct (* {{{ *) ("dot -Tpdf -Gcharset=latin1 " ^ fname_dot ^ " -o" ^ fname_ps)) (* }}} *) - exception Expired - type vcs = (branch_type, transaction, state_info) t + type vcs = (branch_type, transaction, vcs state_info) t let vcs : vcs ref = ref (empty Stateid.dummy) let init id = @@ -261,17 +326,22 @@ end = struct (* {{{ *) let merge id ~ours ?into branch = vcs := merge !vcs id ~ours ~theirs:Noop ?into branch let delete_branch branch = vcs := delete_branch !vcs branch + let reset_branch branch id = vcs := reset_branch !vcs branch id let commit id t = vcs := commit !vcs id t + let rewrite_merge id ~ours ~at branch = + vcs := rewrite_merge !vcs id ~ours ~theirs:Noop ~at branch + let reachable id = reachable !vcs id let mk_branch_name (_, _, x) = Branch.make (match x with | VernacDefinition (_,(_,i),_) -> string_of_id i | VernacStartTheoremProof (_,[Some (_,i),_],_) -> string_of_id i | _ -> "branch") - let branch name kind = vcs := branch !vcs name kind + let edit_branch = Branch.make "edit" + let branch ?root ?pos name kind = vcs := branch !vcs ?root ?pos name kind let get_info id = match get_info !vcs id with | Some x -> x - | None -> raise Expired + | None -> raise Vcs_aux.Expired let set_state id s = (get_info id).state <- Some s let forget_state id = (get_info id).state <- None let reached id b = @@ -279,20 +349,23 @@ end = struct (* {{{ *) if b then info.n_reached <- info.n_reached + 1 else info.n_reached <- -1 let goals id n = (get_info id).n_goals <- n - let create_cluster l = vcs := create_cluster !vcs l + let cur_tip () = get_branch_pos (current_branch ()) let proof_nesting () = Vcs_aux.proof_nesting !vcs let checkout_shallowest_proof_branch () = - let pl = proof_nesting () in - try - let branch, mode = match Vcs_aux.find_proof_at_depth !vcs pl with - | h, { Vcs_.kind = `Proof (m, _) } -> h, m | _ -> assert false in - checkout branch; - Proof_global.activate_proof_mode mode - with Failure _ -> - checkout Branch.master; - Proof_global.disactivate_proof_mode "Classic" (* XXX *) + if List.mem edit_branch (Vcs_.branches !vcs) then + checkout edit_branch + else + let pl = proof_nesting () in + try + let branch, mode = match Vcs_aux.find_proof_at_depth !vcs pl with + | h, { Vcs_.kind = `Proof (m, _) } -> h, m | _ -> assert false in + checkout branch; + Proof_global.activate_proof_mode mode + with Failure _ -> + checkout Branch.master; + Proof_global.disactivate_proof_mode "Classic" (* XXX *) (* copies the transaction on every open branch *) let propagate_sideff t = @@ -302,21 +375,7 @@ end = struct (* {{{ *) merge id ~ours:(Sideff t) ~into:b Branch.master) (List.filter (fun b -> not (Branch.equal b Branch.master)) (branches ())) - let visit id = - try - match Dag.from_node (dag !vcs) id with - | [n, Cmd x] -> { step = `Cmd x; next = n } - | [n, Alias x] -> { step = `Alias x; next = n } - | [n, Fork x] -> { step = `Fork x; next = n } - | [n, Qed x; p, Noop] - | [p, Noop; n, Qed x] -> { step = `Qed (x,p); next = n } - | [n, Sideff None; p, Noop] - | [p, Noop; n, Sideff None]-> { step = `Sideff (`Id p); next = n } - | [n, Sideff (Some x); p, Noop] - | [p, Noop; n, Sideff (Some x)]-> { step = `Sideff(`Ast (x,p)); next = n } - | [n, Sideff (Some x)]-> {step = `Sideff(`Ast (x,Stateid.dummy)); next=n} - | _ -> anomaly (str ("Malformed VCS at node "^Stateid.to_string id)) - with Not_found -> raise Expired + let visit id = Vcs_aux.visit !vcs id let slice ~start ~stop ~purify = let l = @@ -335,7 +394,9 @@ end = struct (* {{{ *) (* Stm should have reached the beginning of proof *) assert (not (Option.is_empty info.state)); (* This may be expensive *) - let info = { info with state = Some (purify (Option.get info.state)) } in + let info = { info with + state = Some (purify (Option.get info.state)); + vcs_backup = None,None } in let v = Vcs_.set_info v stop info in List.fold_right (fun (id,tr) v -> let v = Vcs_.commit v id tr in @@ -343,10 +404,17 @@ end = struct (* {{{ *) (* TODO: we could drop all of them but for the last one and purify it, * so that proofs partially executed in the main process are not * completely re-executed in the slave process *) - let info = { info with state = None } in + let info = { info with state = None; vcs_backup = None,None } in let v = Vcs_.set_info v id info in v) l v + let create_cluster l ~tip = vcs := create_cluster !vcs l tip + let cluster_of id = Option.map Dag.Cluster.data (cluster_of !vcs id) + let delete_cluster_of id = + Option.iter (fun x -> vcs := delete_cluster !vcs x) (Vcs_.cluster_of !vcs id) + + let gc () = vcs := gc !vcs + module NB : sig (* Non blocking Sys.command *) val command : (unit -> unit) -> unit @@ -396,20 +464,19 @@ module State : sig (** The function is from unit, so it uses the current state to define a new one. I.e. one may been to install the right state before defining a new one. - - Warning: an optimization requires that state modifying functions - are always executed using this wrapper. *) - val define : ?cache:bool -> (unit -> unit) -> Stateid.t -> unit + Warning: an optimization in installed_cached requires that state + modifying functions are always executed using this wrapper. *) + val define : + ?redefine:bool -> ?cache:bool -> (unit -> unit) -> Stateid.t -> unit val install_cached : Stateid.t -> unit val is_cached : Stateid.t -> bool val exn_on : Stateid.t -> ?valid:Stateid.t -> exn -> exn - (* TODO: rename *) (* projects a state so that it can be marshalled and its content is * sufficient for the execution of Coq on a proof branch *) - val purify : state -> state + val make_shallow : state -> state end = struct (* {{{ *) @@ -419,9 +486,11 @@ end = struct (* {{{ *) (* helpers *) let freeze_global_state marshallable = - States.freeze ~marshallable, Proof_global.freeze ~marshallable - let unfreeze_global_state (s,p) = - States.unfreeze s; Proof_global.unfreeze p + { system = States.freeze ~marshallable; + proof = Proof_global.freeze ~marshallable; + shallow = (marshallable = `Shallow) } + let unfreeze_global_state { system; proof } = + States.unfreeze system; Proof_global.unfreeze proof (* hack to make futures functional *) let in_t, out_t = Dyn.create "state4future" @@ -429,11 +498,12 @@ end = struct (* {{{ *) (fun () -> in_t (freeze_global_state `No, !cur_id)) (fun t -> let s,i = out_t t in unfreeze_global_state s; cur_id := i) - let purify s = - Future.purify (fun s -> - unfreeze_global_state s; - freeze_global_state `Shallow - ) s + let make_shallow s = + if s.shallow then s + else + Future.purify (fun s -> + unfreeze_global_state s; + freeze_global_state `Shallow) s let is_cached id = Stateid.equal id !cur_id || @@ -457,14 +527,15 @@ end = struct (* {{{ *) Pp.feedback ~state_id:id (Interface.ErrorMsg (loc, msg)); Stateid.add e ?valid id - let define ?(cache=false) f id = - if is_cached id then - anomaly (str"defining state "++str(Stateid.to_string id)++str" twice"); + let define ?(redefine=false) ?(cache=false) f 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"); try - prerr_endline ("defining " ^ - Stateid.to_string id ^ " (cache=" ^string_of_bool cache^")"); + prerr_endline("defining "^str_id^" (cache=" ^string_of_bool cache^")"); f (); if cache then freeze `No id; + prerr_endline ("setting cur id to "^str_id); cur_id := id; feedback ~state_id:id Interface.Processed; VCS.reached id true; @@ -489,7 +560,7 @@ module Slaves : sig (* (eventually) remote calls *) val build_proof : exn_info:(Stateid.t * Stateid.t) -> start:Stateid.t -> stop:Stateid.t -> - Entries.proof_output list Future.computation + future_proof (* blocking function that waits for the task queue to be empty *) val wait_all_done : unit -> unit @@ -502,7 +573,8 @@ module Slaves : sig val slave_init_stdout : unit -> unit (* to disentangle modules *) - val set_reach_known_state : (cache:bool -> Stateid.t -> unit) -> unit + val set_reach_known_state : + (?redefine_qed:bool -> cache:bool -> Stateid.t -> unit) -> unit end = struct (* {{{ *) @@ -593,7 +665,7 @@ end = struct (* {{{ *) end (* }}} *) - let reach_known_state = ref (fun ~cache id -> ()) + let reach_known_state = ref (fun ?redefine_qed ~cache id -> ()) let set_reach_known_state f = reach_known_state := f type request = ReqBuildProof of (Stateid.t * Stateid.t) * Stateid.t * VCS.vcs @@ -628,7 +700,7 @@ end = struct (* {{{ *) match task with | TaskBuildProof (exn_info,bop,eop,_) -> ReqBuildProof(exn_info,eop, - VCS.slice ~start:eop ~stop:bop ~purify:State.purify) + VCS.slice ~start:eop ~stop:bop ~purify:State.make_shallow) let build_proof_here (id,valid) eop = Future.create (fun () -> @@ -808,13 +880,14 @@ end (* }}} *) (* Runs all transactions needed to reach a state *) module Reach : sig - val known_state : cache:bool -> Stateid.t -> unit + val known_state : ?redefine_qed:bool -> cache:bool -> Stateid.t -> unit end = struct (* {{{ *) let pstate = ["meta counter"; "evar counter"; "program-tcc-table"] let collect_proof cur hd id = + prerr_endline ("Collecting proof ending at "^Stateid.to_string id); let is_defined = function | _, (_, _, VernacEndProof (Proved (false,_))) -> true | _ -> false in @@ -823,23 +896,25 @@ let collect_proof cur hd id = match last, view.step with | _, `Cmd (x, _) -> collect (Some (id,x)) (id::accn) view.next | _, `Alias _ -> collect None (id::accn) view.next - | Some (parent, (_,_,VernacExactProof _)), `Fork _ -> - `NotOptimizable `Immediate | _, `Fork(_,_,_::_::_)-> `NotOptimizable `MutualProofs (* TODO: enderstand where we need that *) | Some (parent, (_,_,VernacProof(_,Some _) as v)), `Fork (_, hd', _) -> - assert (VCS.Branch.equal hd hd'); + assert (VCS.Branch.equal hd hd' || VCS.Branch.equal hd VCS.edit_branch); `Optimizable (parent, Some v, accn) | Some (parent, _), `Fork (_, hd', _) -> - assert (VCS.Branch.equal hd hd'); + assert (VCS.Branch.equal hd hd' || VCS.Branch.equal hd VCS.edit_branch); `MaybeOptimizable (parent, accn) | _, `Sideff se -> collect None (id::accn) view.next | _ -> `NotOptimizable `Unknown in - if State.is_cached id then `NotOptimizable `Unknown - else if is_defined cur then `NotOptimizable `Transparent - else collect (Some cur) [] id + match cur, (VCS.visit id).step with + | (parent, (_,_,VernacExactProof _)), `Fork _ -> + `NotOptimizable `Immediate + | _ -> + if State.is_cached id then `NotOptimizable `AlreadyEvaluated + else if is_defined cur then `NotOptimizable `Transparent + else collect (Some cur) [] id -let known_state ~cache id = +let known_state ?(redefine_qed=false) ~cache id = (* ugly functions to process nested lemmas, i.e. hard to reproduce * side effects *) @@ -854,9 +929,9 @@ let known_state ~cache id = cherry_pick_non_pstate ()) id (* traverses the dag backward from nodes being already calculated *) - and reach ?(cache=cache) id = + and reach ?(redefine_qed=false) ?(cache=cache) id = prerr_endline ("reaching: " ^ Stateid.to_string id); - if State.is_cached id then begin + if not redefine_qed && State.is_cached id then begin State.install_cached id; feedback ~state_id:id Interface.Processed; prerr_endline ("reached (cache)") @@ -864,73 +939,88 @@ let known_state ~cache id = let step, cache_step = let view = VCS.visit id in match view.step with - | `Alias id -> - (fun () -> - reach view.next; reach id; Vernacentries.try_print_subgoals ()), - cache - | `Cmd (x,_) -> (fun () -> reach view.next; interp id x), cache - | `Fork (x,_,_) -> (fun () -> reach view.next; interp id x), true - | `Qed ((x,keep,(hd,_)), eop) -> - let rec aux = function - | `Optimizable (start, proof_using_ast, nodes) -> - (fun () -> - prerr_endline ("Optimizable " ^ Stateid.to_string id); - VCS.create_cluster nodes; - begin match keep with - | VtKeep -> - reach ~cache:true start; - let fp = - Slaves.build_proof - ~exn_info:(id,eop) ~start ~stop:eop in - let proof = Proof_global.close_future_proof fp in - reach view.next; - interp id ~proof x; - | VtDrop -> - reach view.next; - Option.iter (interp start) proof_using_ast; - interp id x - end; - Proof_global.discard_all ()) - | `NotOptimizable `Immediate -> assert (Stateid.equal view.next eop); - (fun () -> reach eop; interp id x; Proof_global.discard_all ()) - | `NotOptimizable _ -> - (fun () -> - prerr_endline ("NotOptimizable " ^ Stateid.to_string id); - reach eop; - begin match keep with - | VtKeep -> - let proof = Proof_global.close_proof () in - reach view.next; - interp id ~proof x - | VtDrop -> - reach view.next; - interp id x - end; - Proof_global.discard_all ()) - | `MaybeOptimizable (start, nodes) -> - (fun () -> - prerr_endline ("MaybeOptimizable " ^ Stateid.to_string id); - reach ~cache:true start; - (* no sections and no modules *) - if List.is_empty (Environ.named_context (Global.env ())) && - Safe_typing.is_curmod_library (Global.safe_env ()) then - aux (`Optimizable (start, None, nodes)) () - else - aux (`NotOptimizable `Unknown) ()) - in - aux (collect_proof (view.next, x) hd eop), true - | `Sideff (`Ast (x,_)) -> - (fun () -> reach view.next; interp id x), cache - | `Sideff (`Id origin) -> - (fun () -> - let s = pure_cherry_pick_non_pstate origin in - reach view.next; - inject_non_pstate s), - cache + | `Alias id -> (fun () -> + reach view.next; reach id; Vernacentries.try_print_subgoals () + ), cache + | `Cmd (x,_) -> (fun () -> + reach view.next; vernac_interp id x + ), cache + | `Fork (x,_,_) -> (fun () -> + reach view.next; vernac_interp id x + ), true + | `Qed ({ qast = x; keep; brinfo; brname } as qed, eop) -> + let rec aux = function + | `Optimizable (start, proof_using_ast, nodes) -> (fun () -> + prerr_endline ("Optimizable " ^ Stateid.to_string id); + VCS.create_cluster nodes ~tip:id; + begin match keep, brinfo, qed.fproof with + | VtKeep, { VCS.kind = `Edit _ }, None -> assert false + | VtKeep, { VCS.kind = `Edit _ }, Some ofp -> + assert(redefine_qed = true); + VCS.create_cluster nodes ~tip:id; + let fp = + Slaves.build_proof ~exn_info:(id,eop) ~start ~stop:eop in + Future.replace ofp fp; + qed.fproof <- Some fp + | VtKeep, { VCS.kind = `Proof _ }, Some _ -> assert false + | VtKeep, { VCS.kind = `Proof _ }, None -> + reach ~cache:true start; + let fp = + Slaves.build_proof ~exn_info:(id,eop) ~start ~stop:eop in + qed.fproof <- Some fp; + let proof = Proof_global.close_future_proof fp in + reach view.next; + vernac_interp id ~proof x + | VtDrop, _, _ -> + reach view.next; + Option.iter (vernac_interp start) proof_using_ast; + vernac_interp id x + | _, { VCS.kind = `Master }, _ -> assert false + end; + Proof_global.discard_all () + ), not redefine_qed + | `NotOptimizable `Immediate -> (fun () -> + assert (Stateid.equal view.next eop); + reach eop; vernac_interp id x; Proof_global.discard_all () + ), true + | `NotOptimizable _ -> (fun () -> + prerr_endline ("NotOptimizable " ^ Stateid.to_string id); + reach eop; + begin match keep with + | VtKeep -> + let proof = Proof_global.close_proof () in + reach view.next; + vernac_interp id ~proof x + | VtDrop -> + reach view.next; + vernac_interp id x + end; + Proof_global.discard_all () + ), true + | `MaybeOptimizable (start, nodes) -> (fun () -> + prerr_endline ("MaybeOptimizable " ^ Stateid.to_string id); + reach ~cache:true start; + (* no sections and no modules *) + if List.is_empty (Environ.named_context (Global.env ())) && + Safe_typing.is_curmod_library (Global.safe_env ()) then + fst (aux (`Optimizable (start, None, nodes))) () + else + fst (aux (`NotOptimizable `Unknown)) () + ), not redefine_qed + in + aux (collect_proof (view.next, x) brname eop) + | `Sideff (`Ast (x,_)) -> (fun () -> + reach view.next; vernac_interp id x + ), cache + | `Sideff (`Id origin) -> (fun () -> + let s = pure_cherry_pick_non_pstate origin in + reach view.next; + inject_non_pstate s + ), cache in - State.define ~cache:cache_step step id; + State.define ~cache:cache_step ~redefine:redefine_qed step id; prerr_endline ("reached: "^ Stateid.to_string id) in - reach id + reach ~redefine_qed id end (* }}} *) let _ = Slaves.set_reach_known_state Reach.known_state @@ -940,59 +1030,59 @@ module Backtrack : sig val record : unit -> unit val backto : Stateid.t -> unit - val cur : unit -> Stateid.t (* we could navigate the dag, but this ways easy *) - val branches_of : Stateid.t -> VCS.Branch.t list + val branches_of : Stateid.t -> backup (* To be installed during initialization *) val undo_vernac_classifier : vernac_expr -> vernac_classification end = struct (* {{{ *) - module S = Stack - - type hystory_elt = { - id : Stateid.t ; - vcs : VCS.vcs; - label : Names.Id.t list; (* To implement a limited form of reset *) - } - - let history : hystory_elt S.t = S.create () - - let cur () = - if S.is_empty history then anomaly (str "Empty history"); - (S.top history).id - let record () = - let id = VCS.get_branch_pos (VCS.current_branch ()) in - S.push { - id = id; - vcs = VCS.backup (); - label = - if Stateid.equal id Stateid.initial || Stateid.equal id Stateid.dummy then [] else - match VCS.visit id with - | { step = `Fork (_,_,l) } -> l - | { step = `Cmd (_,l) } -> l - | _ -> [] } - history + let current_branch = VCS.current_branch () in + let mine = current_branch, VCS.get_branch current_branch in + let info = VCS.get_info (VCS.get_branch_pos current_branch) in + let others = + CList.map_filter (fun b -> + if Vcs_.Branch.equal b current_branch then None + else Some(b, VCS.get_branch b)) (VCS.branches ()) in + info.vcs_backup <- Some (VCS.backup ()), Some { mine; others } let backto oid = - assert(not (S.is_empty history)); - let rec pop_until_oid () = - let id = (S.top history).id in - if Stateid.equal id Stateid.initial || Stateid.equal id oid then () - else begin ignore (S.pop history); pop_until_oid (); end in - pop_until_oid (); - let backup = S.top history in - VCS.restore backup.vcs; - if not (Stateid.equal backup.id oid) then anomaly (str "Backto an unknown state") + let info = VCS.get_info oid in + match info.vcs_backup with + | None, _ -> anomaly(str"Backtrack.backto to a state with no vcs_backup") + | Some vcs, _ -> + VCS.restore vcs; + let id = VCS.get_branch_pos (VCS.current_branch ()) in + if not (Stateid.equal id oid) then + anomaly (str "Backtrack.backto did not jump to the right state") let branches_of id = - try - let s = S.find (fun s -> Stateid.equal s.id id) history in - Vcs_.branches s.vcs - with Not_found -> assert false + let info = VCS.get_info id in + match info.vcs_backup with + | _, None -> + anomaly(str"Backtrack.branches_of on a state with no vcs_backup") + | _, Some x -> x + + let rec fold_until f acc id = + let next acc = + if id = Stateid.initial then raise Not_found + else fold_until f acc (VCS.visit id).next in + let info = VCS.get_info id in + match info.vcs_backup with + | None, _ -> next acc + | Some vcs, _ -> + let ids = + if id = Stateid.initial || id = Stateid.dummy then [] else + match VCS.visit id with + | { step = `Fork (_,_,l) } -> l + | { step = `Cmd (_,l) } -> l + | _ -> [] in + match f acc (id, vcs, ids) with + | Stop x -> x + | Next acc -> next acc let undo_vernac_classifier = function | VernacResetInitial -> @@ -1000,39 +1090,47 @@ end = struct (* {{{ *) | VernacResetName (_,name) -> msg_warning (str"Reset not implemented for automatically generated constants"); + let id = VCS.get_branch_pos (VCS.current_branch ()) in (try - let s = - S.seek (fun b s -> - if b then Stop s else Next (List.mem name s.label)) - false history in - VtStm (VtBack s.id, true), VtNow + let oid = + fold_until (fun b (id,_,label) -> + if b then Stop id else Next (List.mem name label)) + false id in + VtStm (VtBack oid, true), VtNow with Not_found -> - VtStm (VtBack (S.top history).id, true), VtNow) + VtStm (VtBack id, true), VtNow) | VernacBack n -> - let s = S.seek (fun n s -> - if Int.equal n 0 then Stop s else Next (n-1)) n history in - VtStm (VtBack s.id, true), VtNow + let id = VCS.get_branch_pos (VCS.current_branch ()) in + let oid = fold_until (fun n (id,_,_) -> + if Int.equal n 0 then Stop id else Next (n-1)) n id in + VtStm (VtBack oid, true), VtNow | VernacUndo n -> - let s = S.seek (fun n s -> - if Int.equal n 0 then Stop s else Next (n-1)) n history in - VtStm (VtBack s.id, true), VtLater + let id = VCS.get_branch_pos (VCS.current_branch ()) in + let oid = fold_until (fun n (id,_,_) -> + if Int.equal n 0 then Stop id else Next (n-1)) n id in + VtStm (VtBack oid, true), VtLater | VernacUndoTo _ | VernacRestart as e -> let m = match e with VernacUndoTo m -> m | _ -> 0 in - let vcs = (S.top history).vcs in + 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") + | Some vcs, _ -> vcs in let cb, _ = Vcs_aux.find_proof_at_depth vcs (Vcs_aux.proof_nesting vcs) in - let n = S.seek (fun n { vcs } -> + let n = fold_until (fun n (_,vcs,_) -> if List.mem cb (Vcs_.branches vcs) then Next (n+1) else Stop n) - 0 history in - let s = S.seek (fun n s -> - if Int.equal n 0 then Stop s else Next (n-1)) (n-m-1) history in - VtStm (VtBack s.id, true), VtLater + 0 id in + let oid = fold_until (fun n (id,_,_) -> + if Int.equal n 0 then Stop id else Next (n-1)) (n-m-1) id in + VtStm (VtBack oid, true), VtLater | VernacAbortAll -> - let s = S.find (fun s -> - match Vcs_.branches s.vcs with [_] -> true | _ -> false) - history in - VtStm (VtBack s.id, true), VtLater + let id = VCS.get_branch_pos (VCS.current_branch ()) in + let oid = fold_until (fun () (id,vcs,_) -> + match Vcs_.branches vcs with [_] -> Stop id | _ -> Next ()) + () id in + VtStm (VtBack oid, true), VtLater | VernacBacktrack (id,_,_) | VernacBackTo id -> VtStm (VtBack (Stateid.of_int id), true), VtNow @@ -1040,9 +1138,6 @@ end = struct (* {{{ *) end (* }}} *) -let slave_main_loop () = Slaves.slave_main_loop () -let slave_init_stdout () = Slaves.slave_init_stdout () - let init () = VCS.init Stateid.initial; set_undo_classifier Backtrack.undo_vernac_classifier; @@ -1050,6 +1145,10 @@ let init () = Backtrack.record (); if Int.equal !Flags.coq_slave_mode 0 then Slaves.init () +let slave_main_loop () = Slaves.slave_main_loop () + +let slave_init_stdout () = Slaves.slave_init_stdout () + let observe id = let vcs = VCS.backup () in try @@ -1065,18 +1164,15 @@ let finish () = observe (VCS.get_branch_pos (VCS.current_branch ())); VCS.print () -let join_aborted_proofs () = - let rec aux id = - if Stateid.equal id Stateid.initial then () else - let view = VCS.visit id in - match view.step with - | `Qed ((_,VtDrop,_),eop) -> - Future.purify observe eop; aux view.next - | `Sideff _ | `Alias _ | `Cmd _ | `Fork _ | `Qed _ -> aux view.next - in - aux (VCS.get_branch_pos VCS.Branch.master) - let join () = + let rec jab id = + if Stateid.equal id Stateid.initial then () + else + let view = VCS.visit id in + match view.step with + | `Qed ({ keep = VtDrop }, eop) -> + Future.purify observe eop; jab view.next + | _ -> jab view.next in finish (); VCS.print (); Slaves.wait_all_done (); @@ -1084,69 +1180,88 @@ let join () = Global.join_safe_environment (); VCS.print (); prerr_endline "Joining the aborted proofs"; - join_aborted_proofs (); + jab (VCS.get_branch_pos VCS.Branch.master); VCS.print () -let merge_proof_branch x keep branch = - if VCS.Branch.equal branch VCS.Branch.master then - raise(State.exn_on Stateid.dummy Proof_global.NoCurrentProof); - let info = VCS.get_branch branch in - VCS.checkout VCS.Branch.master; - let id = VCS.new_node () in - VCS.merge id ~ours:(Qed (x,keep,(branch, info))) branch; - VCS.delete_branch branch; - if keep == VtKeep then VCS.propagate_sideff None - -let process_transaction verbosely (loc, expr) = +let merge_proof_branch qast keep brname = + let brinfo = VCS.get_branch brname in + let qed fproof = { qast; keep; brname; brinfo; fproof } in + match brinfo with + | { VCS.kind = `Proof _ } -> + VCS.checkout VCS.Branch.master; + let id = VCS.new_node () in + VCS.merge id ~ours:(Qed (qed None)) brname; + VCS.delete_branch brname; + if keep == VtKeep then VCS.propagate_sideff None; + `Ok + | { VCS.kind = `Edit (mode, qed_id, master_id) } -> + let ofp = + match VCS.visit qed_id with + | { step = `Qed ({ fproof }, _) } -> fproof + | _ -> assert false in + VCS.rewrite_merge qed_id ~ours:(Qed (qed ofp)) ~at:master_id brname; + VCS.delete_branch brname; + VCS.gc (); + Reach.known_state ~redefine_qed:true ~cache:false qed_id; + VCS.checkout VCS.Branch.master; + `Unfocus qed_id + | { VCS.kind = `Master } -> + raise (State.exn_on Stateid.dummy Proof_global.NoCurrentProof) + +let process_transaction ~tty verbose c (loc, expr) = let warn_if_pos a b = if b then msg_warning(pr_ast a ++ str" should not be part of a script") in - let v, x = expr, (verbosely && Flags.is_verbose(), loc, expr) in + let v, x = expr, (verbose && Flags.is_verbose(), loc, expr) in prerr_endline ("{{{ execute: "^ string_of_ppcmds (pr_ast x)); let vcs = VCS.backup () in try let head = VCS.current_branch () in VCS.checkout head; - begin - let c = classify_vernac v in + let rc = begin prerr_endline (" classified as: " ^ string_of_vernac_classification c); match c with (* Joining various parts of the document *) - | VtStm (VtJoinDocument, b), VtNow -> warn_if_pos x b; join () - | VtStm (VtFinish, b), VtNow -> warn_if_pos x b; finish () - | VtStm (VtObserve id, b), VtNow -> warn_if_pos x b; observe id + | VtStm (VtJoinDocument, b), VtNow -> warn_if_pos x b; join (); `Ok + | VtStm (VtFinish, b), VtNow -> warn_if_pos x b; finish (); `Ok + | VtStm (VtObserve id, b), VtNow -> warn_if_pos x b; observe id; `Ok | VtStm ((VtObserve _ | VtFinish | VtJoinDocument), _), VtLater -> anomaly(str"classifier: join actions cannot be classified as VtLater") (* Back *) | VtStm (VtBack oid, true), w -> let id = VCS.new_node () in - let bl = Backtrack.branches_of oid in + let { mine; others } = Backtrack.branches_of oid in List.iter (fun branch -> - if not (List.mem branch bl) then - merge_proof_branch x VtDrop branch) + if not (List.mem_assoc branch (mine::others)) then + ignore(merge_proof_branch x VtDrop branch)) (VCS.branches ()); VCS.checkout_shallowest_proof_branch (); VCS.commit id (Alias oid); - Backtrack.record (); if w == VtNow then finish () + Backtrack.record (); if w == VtNow then finish (); `Ok | VtStm (VtBack id, false), VtNow -> prerr_endline ("undo to state " ^ Stateid.to_string id); Backtrack.backto id; VCS.checkout_shallowest_proof_branch (); - Reach.known_state ~cache:(interactive ()) id; + Reach.known_state ~cache:(interactive ()) id; `Ok | VtStm (VtBack id, false), VtLater -> anomaly(str"classifier: VtBack + VtLater must imply part_of_script") (* Query *) - | VtQuery false, VtNow -> + | VtQuery false, VtNow when tty = true -> finish (); - (try Future.purify (interp Stateid.dummy) (true,loc,expr) + (try Future.purify (vernac_interp Stateid.dummy) (true,loc,expr) with e when Errors.noncritical e -> let e = Errors.push e in - raise(State.exn_on Stateid.dummy e)) + raise(State.exn_on Stateid.dummy e)); `Ok + | VtQuery false, VtNow -> + (try vernac_interp Stateid.dummy x + with e when Errors.noncritical e -> + let e = Errors.push e in + raise(State.exn_on Stateid.dummy e)); `Ok | VtQuery true, w -> let id = VCS.new_node () in VCS.commit id (Cmd (x,[])); - Backtrack.record (); if w == VtNow then finish () + Backtrack.record (); if w == VtNow then finish (); `Ok | VtQuery false, VtLater -> anomaly(str"classifier: VtQuery + VtLater must imply part_of_script") @@ -1158,15 +1273,16 @@ let process_transaction verbosely (loc, expr) = VCS.commit id (Fork (x, bname, names)); VCS.branch bname (`Proof (mode, VCS.proof_nesting () + 1)); Proof_global.activate_proof_mode mode; - Backtrack.record (); if w == VtNow then finish () + Backtrack.record (); if w == VtNow then finish (); `Ok | VtProofStep, w -> let id = VCS.new_node () in VCS.commit id (Cmd (x,[])); - Backtrack.record (); if w == VtNow then finish () + Backtrack.record (); if w == VtNow then finish (); `Ok | VtQed keep, w -> - merge_proof_branch x keep head; + let rc = merge_proof_branch x keep head in VCS.checkout_shallowest_proof_branch (); - Backtrack.record (); if w == VtNow then finish () + Backtrack.record (); if w == VtNow then finish (); + rc (* Side effect on all branches *) | VtSideff l, w -> @@ -1175,7 +1291,7 @@ let process_transaction verbosely (loc, expr) = VCS.commit id (Cmd (x,l)); VCS.propagate_sideff (Some x); VCS.checkout_shallowest_proof_branch (); - Backtrack.record (); if w == VtNow then finish () + Backtrack.record (); if w == VtNow then finish (); `Ok (* Unknown: we execute it, check for open goals and propagate sideeff *) | VtUnknown, VtNow -> @@ -1184,7 +1300,7 @@ let process_transaction verbosely (loc, expr) = VCS.checkout VCS.Branch.master; let mid = VCS.get_branch_pos VCS.Branch.master in Reach.known_state ~cache:(interactive ()) mid; - interp id x; + vernac_interp id x; (* Vernac x may or may not start a proof *) if VCS.Branch.equal head VCS.Branch.master && Proof_global.there_are_pending_proofs () @@ -1199,21 +1315,22 @@ let process_transaction verbosely (loc, expr) = VCS.checkout_shallowest_proof_branch (); end in State.define ~cache:true step id; - Backtrack.record () + Backtrack.record (); `Ok | VtUnknown, VtLater -> anomaly(str"classifier: VtUnknown must imply VtNow") - end; + end in (* Proof General *) begin match v with | VernacStm (PGLast _) -> if not (VCS.Branch.equal head VCS.Branch.master) then - interp Stateid.dummy + vernac_interp Stateid.dummy (true,Loc.ghost,VernacShow (ShowGoal OpenSubgoals)) | _ -> () end; prerr_endline "executed }}}"; - VCS.print () + VCS.print (); + rc with e -> let e = Errors.push e in match Stateid.get e with @@ -1222,22 +1339,163 @@ let process_transaction verbosely (loc, expr) = VCS.print (); anomaly (str ("execute: "^ string_of_ppcmds (Errors.print_no_report e) ^ "}}}")) + | Some (safe_id, id) -> + prerr_endline ("Failed at state " ^ Stateid.to_string id); + VCS.restore vcs; + if tty 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 else begin + VCS.print () + end; + raise e + +(** STM interface {{{******************************************************* **) + +let query ~at s = + Future.purify (fun s -> + if Stateid.equal at Stateid.dummy then finish () + else Reach.known_state ~cache:true at; + let loc_ast = vernac_parse 0 s in + ignore(process_transaction ~tty:false true (VtQuery false, VtNow) loc_ast)) + s + +let add ~ontop ?(check=ignore) verb eid s = + let cur_tip = VCS.cur_tip () in + if Stateid.equal ontop cur_tip then begin + let _, ast as loc_ast = vernac_parse eid s in + check(loc_ast); + let clas = classify_vernac ast in + match process_transaction ~tty:false verb clas loc_ast with + | `Ok -> VCS.cur_tip (), `NewTip + | `Unfocus qed_id -> qed_id, `Unfocus (VCS.cur_tip ()) + end else begin + (* For now, arbitrary edits should be announced with edit_at *) + anomaly(str"Not yet implemented, the GUI should not try this") + end + +type focus = { + start : Stateid.t; + stop : Stateid.t; + tip : Stateid.t +} + +let edit_at id = + let vcs = VCS.backup () in + let on_cur_branch id = + let rec aux cur = + if id = cur then true + else match VCS.visit cur with + | { step = `Fork _ } -> false + | { next } -> aux next in + aux (VCS.get_branch_pos (VCS.current_branch ())) in + let is_ancestor_of_cur_branch id = + Vcs_.NodeSet.mem id + (VCS.reachable (VCS.get_branch_pos (VCS.current_branch ()))) in + let has_failed qed_id = + match VCS.visit qed_id with + | { step = `Qed ({ fproof = Some fp }, _) } -> Future.is_exn fp + | _ -> false in + let rec master_for_br root tip = + if Stateid.equal tip Stateid.initial then tip else + match VCS.visit tip with + | { next } when next = root -> root + | { step = `Fork _ } -> tip + | { step = `Sideff (`Ast(_,id)|`Id id) } -> id + | { next } -> master_for_br root next in + let reopen_branch at_id mode qed_id tip = + VCS.delete_cluster_of id; + let master_id = + match VCS.visit qed_id with + | { step = `Qed _; next = master_id } -> master_id + | _ -> anomaly (str "Cluster not ending with Qed") in + VCS.branch ~root:master_id ~pos:id + VCS.edit_branch (`Edit (mode, qed_id, master_id)); + Reach.known_state ~cache:(interactive ()) id; + VCS.checkout_shallowest_proof_branch (); + `Focus { stop = qed_id; start = master_id; tip } in + let backto id = + List.iter VCS.delete_branch (VCS.branches ()); + let ancestors = VCS.reachable id in + let { mine = brname, brinfo; others } = Backtrack.branches_of id in + List.iter (fun (name,{ VCS.kind = k; root; pos }) -> + if not(VCS.Branch.equal name VCS.Branch.master) && + Vcs_.NodeSet.mem root ancestors then + VCS.branch ~root ~pos name k) + others; + VCS.reset_branch VCS.Branch.master (master_for_br brinfo.VCS.root id); + VCS.branch ~root:brinfo.VCS.root ~pos:brinfo.VCS.pos brname brinfo.VCS.kind; + VCS.delete_cluster_of id; + VCS.gc (); + Reach.known_state ~cache:(interactive ()) id; + VCS.checkout_shallowest_proof_branch (); + `NewTip in + try + let rc = + let focused = List.exists ((=) VCS.edit_branch) (VCS.branches ()) in + let branch_info = + match snd (VCS.get_info id).vcs_backup with + | Some{ mine = _, { VCS.kind = (`Proof(m,_)|`Edit(m,_,_)) }} -> Some m + | _ -> None in + match focused, VCS.cluster_of id, branch_info with + | _, Some _, None -> assert false + | false, Some qed_id, Some mode -> + let tip = VCS.cur_tip () in + if has_failed qed_id then reopen_branch id mode qed_id tip + else backto id + | true, Some qed_id, Some mode -> + if on_cur_branch id then begin + assert false + end else if is_ancestor_of_cur_branch id then begin + backto id + end else begin + anomaly(str"Cannot leave an `Edit branch open") + end + | true, None, _ -> + if on_cur_branch id then begin + VCS.reset_branch (VCS.current_branch ()) id; + Reach.known_state ~cache:(interactive ()) id; + VCS.checkout_shallowest_proof_branch (); + `NewTip + end else if is_ancestor_of_cur_branch id then begin + backto id + end else begin + anomaly(str"Cannot leave an `Edit branch open") + end + | false, None, _ -> backto id + in + VCS.print (); + rc + with e -> + let e = Errors.push e in + match Stateid.get e with + | None -> + VCS.print (); + anomaly (str ("edit_at: "^string_of_ppcmds (Errors.print_no_report e))) | Some (_, id) -> prerr_endline ("Failed at state " ^ Stateid.to_string id); VCS.restore vcs; VCS.print (); raise e +(* }}} *) -(* Query API *) +(** Old tty interface {{{*************************************************** **) -let get_current_state () = Backtrack.cur () +let interp verb (_,e as lexpr) = + let clas = classify_vernac e in + let rc = process_transaction ~tty:true verb clas lexpr in + if rc <> `Ok then anomaly(str"tty loop can't be mixed with the STM protocol"); + if interactive () then finish () + +let get_current_state () = VCS.cur_tip () let current_proof_depth () = let head = VCS.current_branch () in match VCS.get_branch head with | { VCS.kind = `Master } -> 0 - | { VCS.pos = cur; VCS.kind = `Proof (_,n); VCS.root = root } -> - let rec distance cur = + | { VCS.pos = cur; VCS.kind = (`Proof _ | `Edit _); VCS.root = root } -> + let rec distance root = if Stateid.equal cur root then 0 else 1 + distance (VCS.visit cur).next in distance cur @@ -1248,7 +1506,7 @@ let unmangle n = Names.id_of_string (String.sub n idx (String.length n - idx)) let proofname b = match VCS.get_branch b with - | { VCS.kind = `Proof _ } -> Some b + | { VCS.kind = (`Proof _| `Edit _) } -> Some b | _ -> None let get_all_proof_names () = @@ -1263,7 +1521,7 @@ let get_script prf = let view = VCS.visit id in match view.step with | `Fork (_,_,ns) when List.mem prf ns -> acc - | `Qed ((x,_,_), proof) -> find [pi3 x, (VCS.get_info id).n_goals] proof + | `Qed (qed, proof) -> find [pi3 qed.qast, (VCS.get_info id).n_goals] proof | `Sideff (`Ast (x,_)) -> find ((pi3 x, (VCS.get_info id).n_goals)::acc) view.next | `Sideff (`Id id) -> find acc id @@ -1334,4 +1592,6 @@ let show_script ?proof () = let () = Vernacentries.show_script := show_script +(* }}} *) + (* vim:set foldmethod=marker: *) diff --git a/toplevel/stm.mli b/toplevel/stm.mli index ca8c86d580..3c5bd63db4 100644 --- a/toplevel/stm.mli +++ b/toplevel/stm.mli @@ -6,24 +6,58 @@ (* * GNU Lesser General Public License Version 2.1 *) (************************************************************************) -(* Add a transaction (an edit that adds a new line to the script). - The bool is there for -compile-verbose *) -val process_transaction : bool -> Vernacexpr.located_vernac_expr -> unit +open Vernacexpr +open Interface +open Names + +(** 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]. *) +val add : ontop:Stateid.t -> ?check:(located_vernac_expr -> unit) -> + bool -> edit_id -> string -> + Stateid.t * [ `NewTip | `Unfocus of Stateid.t ] + +(* parses and xecutes a command at a given state, throws away its side effects + but for the printings *) +val query : at:Stateid.t -> string -> 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 + [id] is dropped by Coq. [`Focus fo] is returned to say that [fo.tip] is the + new document tip, the document between [id] and [fo.stop] has been dropped. + The portion between [fo.stop] and [fo.tip] has been kept. [fo.start] is + just to tell the gui where the editing zone starts, in case it wants to + graphically denote it. All subsequent [add] happen on top of [id]. *) +type focus = { start : Stateid.t; stop : Stateid.t; tip : Stateid.t } +val edit_at : Stateid.t -> [ `NewTip | `Focus of focus ] (* Evaluates the tip of the current branch *) val finish : unit -> unit -(* Evaluates a particular state id (does not move the current tip) *) -val observe : Stateid.t -> unit - (* Joins the entire document. Implies finish, but also checks proofs *) val join : unit -> unit +(* Id of the tip of the current branch *) val get_current_state : unit -> Stateid.t -val current_proof_depth : unit -> int -val get_all_proof_names : unit -> Names.identifier list -val get_current_proof_name : unit -> Names.identifier option +(* Misc *) val init : unit -> unit val slave_main_loop : unit -> unit val slave_init_stdout : unit -> unit + +(** 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 is the system + is running interactively (-emacs or coqtop). *) +val interp : bool -> located_vernac_expr -> unit + +(* Queries for backward compatibility *) +val current_proof_depth : unit -> int +val get_all_proof_names : unit -> Id.t list +val get_current_proof_name : unit -> Id.t option + diff --git a/toplevel/toplevel.ml b/toplevel/toplevel.ml index 0ca024ccb1..3bcf935ccb 100644 --- a/toplevel/toplevel.ml +++ b/toplevel/toplevel.ml @@ -307,8 +307,7 @@ let do_vernac () = if !print_emacs then msgerr (str (top_buffer.prompt())); resynch_buffer top_buffer; try - Vernac.eval_expr (read_sentence ()); - Stm.finish () + Vernac.eval_expr (read_sentence ()) with | End_of_input | Errors.Quit -> msgerrnl (mt ()); pp_flush(); raise Errors.Quit @@ -318,14 +317,7 @@ let do_vernac () = | any -> Format.set_formatter_out_channel stdout; ppnl (print_toplevel_error any); - pp_flush (); - match Stateid.get any with - | Some (valid_id,_) -> - let valid = Stateid.to_int valid_id in - Vernac.eval_expr (Loc.ghost, - (Vernacexpr.VernacStm (Vernacexpr.Command - (Vernacexpr.VernacBackTo valid)))) - | _ -> () + pp_flush () (** Main coq loop : read vernacular expressions until Drop is entered. Ctrl-C is handled internally as Sys.Break instead of aborting Coq. diff --git a/toplevel/vernac.ml b/toplevel/vernac.ml index 45c0f68135..eaa5b75494 100644 --- a/toplevel/vernac.ml +++ b/toplevel/vernac.ml @@ -268,7 +268,7 @@ let rec vernac_com verbosely checknav (loc,com) = | v when !just_parsing -> () - | v -> Stm.process_transaction verbosely (loc,v) + | v -> Stm.interp verbosely (loc,v) in try checknav loc com; |
