1 (* Copyright (C) 2004, HELM Team.
3 * This file is part of HELM, an Hypertextual, Electronic
4 * Library of Mathematics, developed at the Computer Science
5 * Department, University of Bologna, Italy.
7 * HELM is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * HELM is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with HELM; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 * For details, see the HELM World-Wide-Web page,
23 * http://helm.cs.unibo.it/
28 exception History_failure
29 exception No_goal_left
30 exception Uri_redefinition
31 type event = [ `Proof_changed | `Proof_completed ]
32 let all_events = [ `Proof_changed; `Proof_completed ]
33 let default_events: event list = [ `Proof_changed ]
36 (ProofEngineTypes.status * 'a) option -> (ProofEngineTypes.status * 'a) ->
38 type observer_id = int
40 exception Observer_failures of (observer_id * exn) list
41 exception Tactic_failure of exn
42 exception Data_failure of exn
44 class ['a] history size =
45 let unsome = function Some x -> x | None -> assert false in
48 val history_data = Array.create (size + 1) None
50 val mutable history_hd = 0 (* rightmost index *)
51 val mutable history_cur = 0 (* current index *)
52 val mutable history_tl = 0 (* leftmost index *)
54 method private is_empty = history_data.(history_cur) = None
56 method push (status: 'a) =
58 history_data.(history_cur) <- Some status
60 history_cur <- (history_cur + 1) mod size;
61 history_data.(history_cur) <- Some status;
62 history_hd <- history_cur; (* throw away fake future line *)
63 if history_hd = history_tl then (* tail overwritten *)
64 history_tl <- (history_tl + 1) mod size
67 method undo = function
68 | 0 -> unsome history_data.(history_cur)
69 | steps when steps > 0 ->
71 if history_cur >= history_tl then
72 history_cur - history_tl
74 history_cur + (size - history_tl)
76 if steps > max_undo_steps then
77 raise History_failure;
78 history_cur <- history_cur - steps;
79 if history_cur < 0 then (* fix underflow *)
80 history_cur <- size + history_cur;
81 unsome history_data.(history_cur)
82 | steps (* when steps > 0 *) -> self#redo ~-steps
84 method redo = function
85 | 0 -> unsome history_data.(history_cur)
86 | steps when steps > 0 ->
88 if history_hd >= history_cur then
89 history_hd - history_cur
91 history_hd + (size - history_cur)
93 if steps > max_redo_steps then
94 raise History_failure;
95 history_cur <- (history_cur + steps) mod size;
96 unsome history_data.(history_cur)
97 | steps (* when steps > 0 *) -> self#undo ~-steps
102 ?uri ~typ ?(body = Cic.Meta (1, [])) ?(metasenv = [1,[],typ])
103 init_data compute_data ()
105 let next_observer_id =
106 let next_id = ref 0 in
111 let initial_proof = ((uri: UriManager.uri option), metasenv, body, typ) in
112 let next_goal (goals, proof) =
113 match goals, proof with
114 | goal :: _, _ -> Some goal
115 | [], (_, (goal, _, _) :: _, _, _) ->
116 (* the tactic left no open goal: let's choose the first open goal *)
120 let initial_goal = next_goal ([], initial_proof) in
123 val mutable _proof = initial_proof
124 val mutable _goal = initial_goal
125 val mutable _data: 'a = init_data (initial_proof, initial_goal)
127 (* event -> (id, observer) list *)
128 val observers = Hashtbl.create 7
130 (* assumption: all items in history are uncompleted proofs, thus option on
131 * goal could be ignored and goal are stored as bare integers *)
132 val history = new history history_size
135 history#push self#internal_status
137 method proof = _proof
139 match _goal with Some goal -> goal | None -> raise No_goal_left
140 method private status = (_proof, self#active_goal) (* logic status *)
141 method private set_status (proof, goal) =
145 (* what will be kept in history *)
146 method private internal_status = (self#status, _data)
147 method private set_internal_status (status, data) =
148 self#set_status status;
151 method set_goal goal =
152 let old_internal_status = self#internal_status in
155 self#update_data old_internal_status;
156 history#push self#internal_status;
157 self#notify (Some old_internal_status)
158 with (Data_failure _) as exn ->
159 self#set_internal_status old_internal_status;
162 method uri = let (uri, _, _, _) = _proof in uri
163 method metasenv = let (_, metasenv, _, _) = _proof in metasenv
164 method body = let (_, _, body, _) = _proof in body
165 method typ = let (_, _, _, typ) = _proof in typ
168 let (old_uri, metasenv, body, typ) = _proof in
169 if old_uri <> None then
170 raise Uri_redefinition;
171 _proof <- (Some uri, metasenv, body, typ)
173 method conjecture goal =
174 let (_, metasenv, _, _) = _proof in
175 CicUtil.lookup_meta goal metasenv
177 method private active_goal =
179 | None -> raise No_goal_left
182 method apply_tactic tactic =
183 let old_internal_status = self#internal_status in
184 let (new_proof, new_goals) =
186 ProofEngineTypes.apply_tactic tactic (_proof, self#active_goal)
187 with exn -> raise (Tactic_failure exn)
190 _goal <- next_goal (new_goals, new_proof);
192 self#update_data old_internal_status;
193 history#push self#internal_status;
194 self#notify (Some old_internal_status)
195 with (Data_failure _) as exn ->
196 self#set_internal_status old_internal_status;
199 method proof_completed = _goal <> None
201 method attach_observer ?(interested_in = default_events) observer
203 let id = next_observer_id () in
207 try Hashtbl.find observers event with Not_found -> []
209 Hashtbl.replace observers event ((id, observer)::prev_observers))
213 method detach_observer id =
217 try Hashtbl.find observers event with Not_found -> []
220 List.filter (fun (id', _) -> id' <> id) prev_observers
222 Hashtbl.replace observers event new_observers)
225 method private notify old_internal_status =
226 let cur_internal_status = (self#status, _data) in
228 let notify (id, observer) =
230 observer old_internal_status cur_internal_status
231 with exn -> exns := (id, exn) :: !exns
234 (try Hashtbl.find observers `Proof_changed with Not_found -> []);
235 if self#proof_completed then
237 (try Hashtbl.find observers `Proof_completed with Not_found -> []);
240 | exns -> raise (Observer_failures exns)
242 method private update_data old_internal_status =
243 (* invariant: _goal and/or _proof has been changed
244 * invariant: proof is not yet completed *)
245 let status = self#status in
247 _data <- compute_data old_internal_status status
248 with exn -> raise (Data_failure exn)
250 method undo ?(steps = 1) () =
251 let ((proof, goal), data) = history#undo steps in
257 method redo ?(steps = 1) () = self#undo ~steps:~-steps ()
261 let trivial_status ?uri ~typ ?body ?metasenv () =
262 new status ?uri ~typ ?body ?metasenv (fun _ -> ()) (fun _ _ -> ()) ()