(************************************************************************) (* v * The Coq Proof Assistant / The Coq Development Team *) (* Proofview.proofview -> unfocusable) * (_focus_kind -> bool) type 'a focus_condition = _focus_condition let next_kind = ref 0 let new_focus_kind () = let r = !next_kind in incr next_kind; r (* Auxiliary function to define conditions. *) let check kind1 kind2 = kind1=kind2 (* To be authorized to unfocus one must meet the condition prescribed by the action which focused.*) (* spiwack: we could consider having a list of authorized focus_kind instead of just one, if anyone needs it *) (* [no_cond] only checks that the unfocusing command uses the right [focus_kind]. *) module Cond = struct (* first attempt at an algebra of condition *) (* semantics: - [Cannot] means that the condition is not met - [Strict] that the condition is met - [Loose] that the condition is not quite met but authorises to unfocus provided a condition of a previous focus on the stack is (strictly) met. [Loose] focuses are those, like bullets, which do not have a closing command and are hence closed by unfocusing actions unrelated to their focus_kind. *) let bool e b = if b then fun _ _ -> Strict else fun _ _ -> Cannot e let loose c k p = match c k p with | Cannot _ -> Loose | c -> c let cloose l c = if l then loose c else c let (&&&) c1 c2 k p= match c1 k p , c2 k p with | Cannot e , _ | _ , Cannot e -> Cannot e | Strict, Strict -> Strict | _ , _ -> Loose let kind e k0 k p = bool e (k0=k) k p let pdone e k p = bool e (Proofview.finished p) k p end (* Unfocus command. Fails if the proof is not focused. *) exception CannotUnfocusThisWay let _ = Errors.register_handler begin function | CannotUnfocusThisWay -> Util.error "This proof is focused, but cannot be unfocused this way" | _ -> raise Errors.Unhandled end open Cond let no_cond_gen e ~loose_end k0 = cloose loose_end (kind e k0) let no_cond_gen e ?(loose_end=false) k = no_cond_gen e ~loose_end k , check k let no_cond ?loose_end = no_cond_gen CannotUnfocusThisWay ?loose_end (* [done_cond] checks that the unfocusing command uses the right [focus_kind] and that the focused proofview is complete. *) let done_cond_gen e ~loose_end k0 = (cloose loose_end (kind e k0)) &&& pdone e let done_cond_gen e ?(loose_end=false) k = done_cond_gen e ~loose_end k , check k let done_cond ?loose_end = done_cond_gen CannotUnfocusThisWay ?loose_end (* Subpart of the type of proofs. It contains the parts of the proof which are under control of the undo mechanism *) type proof_state = { (* Current focused proofview *) proofview: Proofview.proofview; (* History of the focusings, provides information on how to unfocus the proof and the extra information stored while focusing. The list is empty when the proof is fully unfocused. *) focus_stack: (_focus_condition*focus_info*Proofview.focus_context) list; (* Extra information which can be freely used to create new behaviours. *) intel: Store.t } type proof_info = { mutable endline_tactic : unit Proofview.tactic ; mutable section_vars : Sign.section_context option; initial_conclusions : Term.types list } type undo_action = | State of proof_state | Effect of (unit -> unit) type proof = { (* current proof_state *) mutable state : proof_state; (* The undo stack *) mutable undo_stack : undo_action list; (* secondary undo stacks used for transactions *) mutable transactions : undo_action list list; info : proof_info } (*** General proof functions ***) let proof { state = p } = let (goals,sigma) = Proofview.proofview p.proofview in (* spiwack: beware, the bottom of the stack is used by [Proof] internally, and should not be exposed. *) let rec map_minus_one f = function | [] -> assert false | [_] -> [] | a::l -> f a :: (map_minus_one f l) in let stack = map_minus_one (fun (_,_,c) -> Proofview.focus_context c) p.focus_stack in (goals,stack,sigma) let rec unroll_focus pv = function | (_,_,ctx)::stk -> unroll_focus (Proofview.unfocus ctx pv) stk | [] -> pv (* spiwack: a proof is considered completed even if its still focused, if the focus doesn't hide any goal. Unfocusing is handled in {!return}. *) let is_done p = Proofview.finished p.state.proofview && Proofview.finished (unroll_focus p.state.proofview p.state.focus_stack) (* spiwack: for compatibility with <= 8.2 proof engine *) let has_unresolved_evar p = Proofview.V82.has_unresolved_evar p.state.proofview (* Returns the list of partial proofs to initial goals *) let partial_proof p = List.map fst (Proofview.return p.state.proofview) (*** The following functions implement the basic internal mechanisms of proofs, they are not meant to be exported in the .mli ***) (* An auxiliary function to push a {!focus_context} on the focus stack. *) let push_focus cond inf context pr = pr.state <- { pr.state with focus_stack = (cond,inf,context)::pr.state.focus_stack } exception FullyUnfocused let _ = Errors.register_handler begin function | FullyUnfocused -> Util.error "The proof is not focused" | _ -> raise Errors.Unhandled end (* An auxiliary function to read the kind of the next focusing step *) let cond_of_focus pr = match pr.state.focus_stack with | (cond,_,_)::_ -> cond | _ -> raise FullyUnfocused (* An auxiliary function to pop and read the last {!Proofview.focus_context} on the focus stack. *) let pop_focus pr = match pr.state.focus_stack with | focus::other_focuses -> pr.state <- { pr.state with focus_stack = other_focuses }; focus | _ -> raise FullyUnfocused (* Auxiliary function to push a [proof_state] onto the undo stack. *) let push_undo save pr = match pr.transactions with | [] -> pr.undo_stack <- save::pr.undo_stack | stack::trans' -> pr.transactions <- (save::stack)::trans' (* Auxiliary function to pop and read a [save_state] from the undo stack. *) exception EmptyUndoStack let _ = Errors.register_handler begin function | EmptyUndoStack -> Util.error "Cannot undo: no more undo information" | _ -> raise Errors.Unhandled end let pop_undo pr = match pr.transactions , pr.undo_stack with | [] , state::stack -> pr.undo_stack <- stack; state | (state::stack)::trans', _ -> pr.transactions <- stack::trans'; state | _ -> raise EmptyUndoStack (* This function focuses the proof [pr] between indices [i] and [j] *) let _focus cond inf i j pr = let (focused,context) = Proofview.focus i j pr.state.proofview in push_focus cond inf context pr; pr.state <- { pr.state with proofview = focused } (* This function unfocuses the proof [pr], it raises [FullyUnfocused], if the proof is already fully unfocused. This function does not care about the condition of the current focus. *) let _unfocus pr = let (_,_,fc) = pop_focus pr in pr.state <- { pr.state with proofview = Proofview.unfocus fc pr.state.proofview } let set_used_variables l p = p.info.section_vars <- Some l let get_used_variables p = p.info.section_vars (*** Endline tactic ***) (* spiwack this is an information about the UI, it might be a good idea to move it to the Proof_global module *) (* Sets the tactic to be used when a tactic line is closed with [...] *) let set_endline_tactic tac p = p.info.endline_tactic <- tac let with_end_tac pr tac = Proofview.tclTHEN tac pr.info.endline_tactic (*** The following functions define the safety mechanism of the proof system, they may be unsafe if not used carefully. There is currently no reason to export them in the .mli ***) (* This functions saves the current state into a [proof_state]. *) let save_state { state = ps } = State ps (* This function stores the current proof state in the undo stack. *) let save pr = push_undo (save_state pr) pr (* This function restores a state, presumably from the top of the undo stack. *) let restore_state save pr = match save with | State save -> pr.state <- save | Effect undo -> undo () (* Interpretes the Undo command. *) let undo pr = (* On a single line, since the effects commute *) restore_state (pop_undo pr) pr (* Adds an undo effect to the undo stack. Use it with care, errors here might result in inconsistent states. *) let add_undo effect pr = push_undo (Effect effect) pr (*** Transactions ***) let init_transaction pr = pr.transactions <- []::pr.transactions let commit_stack pr stack = let push s = push_undo s pr in List.iter push (List.rev stack) (* Invariant: [commit] should be called only when a transaction is open. It closes the current transaction. *) let commit pr = match pr.transactions with | stack::trans' -> pr.transactions <- trans'; commit_stack pr stack | [] -> assert false (* Invariant: [rollback] should be called only when a transaction is open. It closes the current transaction. *) let rec rollback pr = try undo pr; rollback pr with EmptyUndoStack -> match pr.transactions with | []::trans' -> pr.transactions <- trans' | _ -> assert false let transaction pr t = init_transaction pr; try t (); commit pr with reraise -> rollback pr; raise reraise (* Focus command (focuses on the [i]th subgoal) *) (* spiwack: there could also, easily be a focus-on-a-range tactic, is there a need for it? *) let focus cond inf i pr = save pr; _focus cond (Obj.repr inf) i i pr let rec unfocus kind pr () = let starting_point = save_state pr in let cond = cond_of_focus pr in match fst cond kind pr.state.proofview with | Cannot e -> raise e | Strict -> (_unfocus pr; push_undo starting_point pr) | Loose -> begin try _unfocus pr; push_undo starting_point pr; unfocus kind pr () with FullyUnfocused -> raise CannotUnfocusThisWay end let unfocus kind pr = transaction pr (unfocus kind pr) exception NoSuchFocus (* no handler: should not be allowed to reach toplevel. *) let rec get_in_focus_stack kind stack = match stack with | ((_,check),inf,_)::stack -> if check kind then inf else get_in_focus_stack kind stack | [] -> raise NoSuchFocus let get_at_focus kind pr = Obj.magic (get_in_focus_stack kind pr.state.focus_stack) let is_last_focus kind pr = let ((_,check),_,_) = List.hd pr.state.focus_stack in check kind let no_focused_goal p = Proofview.finished p.state.proofview (*** Proof Creation/Termination ***) (* [end_of_stack] is unfocused by return to close every loose focus. *) let end_of_stack_kind = new_focus_kind () let end_of_stack = done_cond_gen FullyUnfocused end_of_stack_kind let unfocused = is_last_focus end_of_stack_kind let start goals = let pr = { state = { proofview = Proofview.init goals ; focus_stack = [] ; intel = Store.empty} ; undo_stack = [] ; transactions = [] ; info = { endline_tactic = Proofview.tclUNIT (); initial_conclusions = List.map snd goals; section_vars = None } } in _focus end_of_stack (Obj.repr ()) 1 (List.length goals) pr; pr exception UnfinishedProof exception HasUnresolvedEvar let _ = Errors.register_handler begin function | UnfinishedProof -> Util.error "Some goals have not been solved." | HasUnresolvedEvar -> Util.error "Some existential variables are uninstantiated." | _ -> raise Errors.Unhandled end let return p = if not (is_done p) then raise UnfinishedProof else if has_unresolved_evar p then (* spiwack: for compatibility with <= 8.3 proof engine *) raise HasUnresolvedEvar else unfocus end_of_stack_kind p; Proofview.return p.state.proofview (*** Function manipulation proof extra informations ***) let get_proof_info pr = pr.state.intel let set_proof_info info pr = save pr; pr.state <- { pr.state with intel=info } (*** Tactics ***) let run_tactic env tac pr = let starting_point = save_state pr in let sp = pr.state.proofview in try let tacticced_proofview = Proofview.apply env tac sp in pr.state <- { pr.state with proofview = tacticced_proofview }; push_undo starting_point pr with reraise -> restore_state starting_point pr; raise reraise (*** Commands ***) let in_proof p k = Proofview.in_proofview p.state.proofview k (*** Compatibility layer with <=v8.2 ***) module V82 = struct let subgoals p = Proofview.V82.goals p.state.proofview let background_subgoals p = Proofview.V82.goals (unroll_focus p.state.proofview p.state.focus_stack) let get_initial_conclusions p = p.info.initial_conclusions let depth p = List.length p.undo_stack let top_goal p = let { Evd.it=gls ; sigma=sigma } = Proofview.V82.top_goals p.state.proofview in { Evd.it=List.hd gls ; sigma=sigma } let top_evars p = Proofview.V82.top_evars p.state.proofview let grab_evars p = if not (is_done p) then raise UnfinishedProof else save p; p.state <- { p.state with proofview = Proofview.V82.grab p.state.proofview } let instantiate_evar n com pr = let starting_point = save_state pr in let sp = pr.state.proofview in try let new_proofview = Proofview.V82.instantiate_evar n com sp in pr.state <- { pr.state with proofview = new_proofview }; push_undo starting_point pr with reraise -> restore_state starting_point pr; raise reraise end