]> matita.cs.unibo.it Git - helm.git/blob - helm/matita/matitaScript.ml
fixed whelp bar
[helm.git] / helm / matita / matitaScript.ml
1 (* Copyright (C) 2004-2005, HELM Team.
2  * 
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.
6  * 
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.
11  * 
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.
16  *
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,
20  * MA  02111-1307, USA.
21  * 
22  * For details, see the HELM World-Wide-Web page,
23  * http://helm.cs.unibo.it/
24  *)
25
26 open Printf
27
28 open MatitaTypes
29
30   (** raised when one of the script margins (top or bottom) is reached *)
31 exception Margin
32
33 let safe_substring s i j =
34   try String.sub s i j with Invalid_argument _ -> assert false
35
36 let heading_nl_RE = Pcre.regexp "^\\s*\n\\s*"
37 let only_dust_RE = Pcre.regexp "^(\\s|\n|%%[^\n]*\n)*$"
38 let multiline_RE = Pcre.regexp "^\n[^\n]+$"
39 let newline_RE = Pcre.regexp "\n"
40  
41 let comment str =
42   if Pcre.pmatch ~rex:multiline_RE str then
43     "\n(** " ^ (Pcre.replace ~rex:newline_RE str) ^ " **)"
44   else
45     "\n(**\n" ^ str ^ "\n**)"
46                      
47 let first_line s =
48   let s = Pcre.replace ~rex:heading_nl_RE s in
49   try
50     let nl_pos = String.index s '\n' in
51     String.sub s 0 nl_pos
52   with Not_found -> s
53
54 let prepend_text header base =
55   if Pcre.pmatch ~rex:heading_nl_RE base then
56     sprintf "\n%s%s" header base
57   else
58     sprintf "%s\n%s" header base
59
60   (** creates a statement AST for the Goal tactic, e.g. "goal 7" *)
61 let goal_ast n =
62   let module A = TacticAst in
63   let loc = CicAst.dummy_floc in
64   A.Executable (loc, A.Tactical (loc, A.Tactic (loc, A.Goal (loc, n))))
65
66 let eval_with_engine status user_goal parsed_text st =
67   let module TA = TacticAst in
68   let module TAPp = TacticAstPp in
69   let parsed_text_length = String.length parsed_text in
70   let loc, ex = 
71     match st with TA.Executable (loc,ex) -> loc, ex | _ -> assert false 
72   in
73   let goal_changed = ref false in
74   let status =
75     match status.proof_status with
76       | Incomplete_proof (_, goal) when goal <> user_goal ->
77           goal_changed := true;
78           MatitaEngine.eval_ast status (goal_ast user_goal)
79       | _ -> status
80   in
81   let new_status = MatitaEngine.eval_ast status st in
82   let new_aliases =
83     match ex with
84       | TA.Command (_, TA.Alias _) ->
85           DisambiguateTypes.Environment.empty
86       | _ -> MatitaSync.alias_diff ~from:status new_status
87   in
88   let new_text =
89     if DisambiguateTypes.Environment.is_empty new_aliases then
90       parsed_text
91     else
92       prepend_text (CicTextualParser2.EnvironmentP3.to_string new_aliases)
93         parsed_text
94   in
95   let new_text =
96     if !goal_changed then
97       prepend_text
98         (TAPp.pp_tactic (TA.Goal (loc, user_goal))(* ^ "\n"*))
99         new_text
100     else
101       new_text
102   in
103     [ new_status, new_text ], parsed_text_length
104
105 let disambiguate term status =
106   let module MD = MatitaDisambiguator in
107   let dbd = MatitaDb.instance () in
108   let metasenv = MatitaMisc.get_proof_metasenv status in
109   let context = MatitaMisc.get_proof_context status in
110   let aliases = MatitaMisc.get_proof_aliases status in
111   let interps = MD.disambiguate_term dbd context metasenv aliases term in
112   match interps with 
113   | [_,_,x,_] -> x
114   | _ -> assert false
115  
116 let eval_macro status (mathviewer:MatitaTypes.mathViewer) urichooser parsed_text mac =
117   let module TA = TacticAst in
118   let module TAPp = TacticAstPp in
119   let module MQ = MetadataQuery in
120   let module MDB = MatitaDb in
121   let module CTC = CicTypeChecker in
122   let module CU = CicUniv in
123   let parsed_text_length = String.length parsed_text in
124   let dbd = MatitaDb.instance () in
125   match mac with
126   (* WHELP's stuff *)
127   | TA.WMatch (loc, term) -> 
128       let term = disambiguate term status in
129       let l =  MQ.match_term ~dbd term in
130       let entry = `Whelp (TAPp.pp_macro_cic (TA.WMatch (loc, term)), l) in
131       mathviewer#show_uri_list ~reuse:true ~entry l;
132       [], parsed_text_length
133   | TA.WInstance (loc, term) ->
134       let term = disambiguate term status in
135       let l = MQ.instance ~dbd term in
136       let entry = `Whelp (TAPp.pp_macro_cic (TA.WInstance (loc, term)), l) in
137       mathviewer#show_uri_list ~reuse:true ~entry l;
138       [], parsed_text_length
139   | TA.WLocate (loc, s) -> 
140       let l = MQ.locate ~dbd s in
141       let entry = `Whelp (TAPp.pp_macro_cic (TA.WLocate (loc, s)), l) in
142       mathviewer#show_uri_list ~reuse:true ~entry l;
143       [], parsed_text_length  
144   | TA.WElim (loc, term) ->
145       let term = disambiguate term status in
146       let uri =
147         match term with
148         | Cic.MutInd (uri,n,_) -> UriManager.string_of_uriref (uri,[n])
149         | _ -> failwith "Not a MutInd"
150       in
151       let l = MQ.elim ~dbd uri in
152       let entry = `Whelp (TAPp.pp_macro_cic (TA.WElim (loc, term)), l) in
153       mathviewer#show_uri_list ~reuse:true ~entry l;
154       [], parsed_text_length  
155   | TA.WHint (loc, term) ->
156       let term = disambiguate term status in
157       let s = ((None,[0,[],term], Cic.Meta (0,[]) ,term),0) in
158       let l = List.map fst (MQ.experimental_hint ~dbd s) in
159       let entry = `Whelp (TAPp.pp_macro_cic (TA.WHint (loc, term)), l) in
160       mathviewer#show_uri_list ~reuse:true ~entry l;
161       [], parsed_text_length  
162   (* REAL macro *)
163   | TA.Hint loc -> 
164       let s = MatitaMisc.get_proof_status status in
165       let l = List.map fst (MQ.experimental_hint ~dbd s) in
166       let selected = urichooser l in
167       (match selected with
168       | [] -> [], parsed_text_length
169       | [uri] -> 
170         let ast = 
171           (TA.Executable (loc,
172             (TA.Tactical (loc, 
173                TA.Tactic (loc,
174                  TA.Apply (loc, CicAst.Uri (uri,None))))))) 
175         in
176         let new_status = MatitaEngine.eval_ast status ast in
177         let extra_text = 
178           comment parsed_text ^ 
179           "\n" ^ TAPp.pp_statement ast
180         in
181         [ new_status , extra_text ],parsed_text_length
182       | _ -> assert false)
183   | TA.Check (_,term) ->
184       let metasenv = MatitaMisc.get_proof_metasenv status in
185       let context = MatitaMisc.get_proof_context status in
186       let aliases = MatitaMisc.get_proof_aliases status in
187       let interps = 
188         MatitaDisambiguator.disambiguate_term 
189           dbd context metasenv aliases term 
190       in
191       let _, metasenv , term, ugraph =
192         match interps with 
193         | [x] -> x
194         | _ -> assert false
195       in
196       let ty,_ = CTC.type_of_aux' metasenv context term ugraph in
197       let t_and_ty = Cic.Cast (term,ty) in
198       mathviewer#show_entry (`Cic (t_and_ty,metasenv));
199       [], parsed_text_length
200   | TA.Quit _ ->
201       failwith "not implemented quit"
202   | _ -> failwith "not implemented"
203
204                                 
205 let eval_executable status (mathviewer:MatitaTypes.mathViewer) urichooser user_goal parsed_text ex =
206   let module TA = TacticAst in
207   let module TAPp = TacticAstPp in
208   let module MD = MatitaDisambiguator in
209   let parsed_text_length = String.length parsed_text in
210   match ex with
211   | TA.Command (loc, _) | TA.Tactical (loc, _) ->
212       eval_with_engine status user_goal parsed_text (TA.Executable (loc, ex))
213   | TA.Macro (_,mac) ->
214       eval_macro status mathviewer urichooser parsed_text mac
215
216 let rec eval_statement status (mathviewer:MatitaTypes.mathViewer) urichooser user_goal s =
217   if Pcre.pmatch ~rex:only_dust_RE s then raise Margin;
218   let st = CicTextualParser2.parse_statement (Stream.of_string s) in
219   let text_of_loc loc =
220     let parsed_text_length = snd (CicAst.loc_of_floc loc) in
221     let parsed_text = safe_substring s 0 parsed_text_length in
222     parsed_text, parsed_text_length
223   in
224   match st with
225   | TacticAst.Comment (loc,_)-> 
226       let parsed_text, parsed_text_length = text_of_loc loc in
227       let remain_len = String.length s - parsed_text_length in
228       let s = String.sub s parsed_text_length remain_len in
229       let s,len = eval_statement status mathviewer urichooser user_goal s in
230       (match s with
231       | (status, text) :: tl ->
232         ((status, parsed_text ^ text)::tl), (parsed_text_length + len)
233       | [] -> [], 0)
234   | TacticAst.Executable (loc, ex) ->
235       let parsed_text, parsed_text_length = text_of_loc loc in
236       eval_executable status mathviewer urichooser user_goal parsed_text ex
237   
238
239 class script ~(buffer: GText.buffer) ~(init: MatitaTypes.status) 
240               ~(mathviewer: MatitaTypes.mathViewer) 
241               ~urichooser () =
242 object (self)
243   initializer self#reset ()
244
245   val mutable statements = [];    (** executed statements *)
246   val mutable history = [ init ];
247     (** list of states before having executed statements. Head element of this
248       * list is the current state, last element is the state at the beginning of
249       * the script.
250       * Invariant: this list length is 1 + length of statements *)
251
252   (** goal as seen by the user (i.e. metano corresponding to current tab) *)
253   val mutable userGoal = ~-1
254
255   (** text mark and tag representing locked part of a script *)
256   val locked_mark =
257     buffer#create_mark ~name:"locked" ~left_gravity:true buffer#start_iter
258   val locked_tag = buffer#create_tag [`BACKGROUND "lightblue"; `EDITABLE false]
259
260     (* history can't be empty, the invariant above grant that it contains at
261      * least the init status *)
262   method status = match history with hd :: _ -> hd | _ -> assert false
263
264   method private _advance ?statement () =
265     let s = match statement with Some s -> s | None -> self#getFuture in
266     MatitaLog.debug ("evaluating: " ^ first_line s ^ " ...");
267     let (entries, parsed_len) = 
268       eval_statement self#status mathviewer urichooser userGoal s in
269     let (new_statuses, new_statements) = List.split entries in
270 (*
271 prerr_endline "evalStatement returned";
272 List.iter (fun s -> prerr_endline ("'" ^ s ^ "'")) new_statements;
273 *)
274     history <- List.rev new_statuses @ history;
275     statements <- List.rev new_statements @ statements;
276     let start = buffer#get_iter_at_mark (`MARK locked_mark) in
277     if statement = None then begin
278       let stop = start#copy#forward_chars parsed_len in
279       buffer#delete ~start ~stop
280     end;
281     let new_text = String.concat "" new_statements in
282     buffer#insert ~iter:start new_text;
283     self#moveMark (String.length new_text);
284
285   method private _retract () =
286     match statements, history with
287     | last_statement :: _, cur_status :: prev_status :: _ ->
288         MatitaSync.time_travel ~present:cur_status ~past:prev_status;
289         statements <- List.tl statements;
290         history <- List.tl history;
291         self#moveMark (- (String.length last_statement))
292     | _ -> raise Margin
293
294   method advance ?statement () =
295     try
296       self#_advance ?statement ()
297     with Margin -> ()
298
299   method retract () = try self#_retract () with Margin -> ()
300
301   method private getFuture =
302     buffer#get_text ~start:(buffer#get_iter_at_mark (`MARK locked_mark))
303       ~stop:buffer#end_iter ()
304
305   (** @param rel_offset relative offset from current position of locked_mark *)
306   method private moveMark rel_offset =
307     let mark = `MARK locked_mark in
308     buffer#remove_tag locked_tag ~start:buffer#start_iter ~stop:buffer#end_iter;
309     let current_mark_pos = buffer#get_iter_at_mark mark in
310     let new_mark_pos =
311       match rel_offset with
312       | 0 -> current_mark_pos
313       | n when n > 0 -> current_mark_pos#forward_chars n
314       | n (* when n < 0 *) -> current_mark_pos#backward_chars (abs n)
315     in
316     buffer#move_mark mark ~where:new_mark_pos;
317     buffer#apply_tag locked_tag ~start:buffer#start_iter ~stop:new_mark_pos;
318     self#notify
319
320   val mutable observers = []
321
322   method addObserver (o: MatitaTypes.status -> unit) =
323     observers <- o :: observers
324
325   method private notify =
326     let status = self#status in
327     List.iter (fun o -> o status) observers
328
329   method loadFrom fname =
330     buffer#set_text (MatitaMisc.input_file fname);
331     self#goto_top
332
333   method saveTo fname =
334     let oc = open_out fname in
335     output_string oc (buffer#get_text ~start:buffer#start_iter
336                         ~stop:buffer#end_iter ());
337     close_out oc
338
339   method private goto_top =
340     MatitaSync.time_travel ~present:self#status ~past:init;
341     statements <- [];
342     history <- [ init ];
343     userGoal <- ~-1;
344     self#notify;
345     buffer#remove_tag locked_tag ~start:buffer#start_iter ~stop:buffer#end_iter;
346     buffer#move_mark (`MARK locked_mark) ~where:buffer#start_iter
347
348   method reset () =
349     self#goto_top;
350     buffer#delete ~start:buffer#start_iter ~stop:buffer#end_iter
351
352   method goto (pos: [`Top | `Bottom | `Cursor]) () =
353     match pos with
354     | `Top -> self#goto_top
355     | `Bottom ->
356         (try while true do self#_advance () done with Margin -> ())
357     | `Cursor ->
358         let locked_iter () = buffer#get_iter_at_mark (`NAME "locked") in
359         let cursor_iter () = buffer#get_iter_at_mark `INSERT in
360         let rec forward_until_cursor () = (* go forward until locked > cursor *)
361           self#_advance ();
362           if (locked_iter ())#compare (cursor_iter ()) < 0 then
363             forward_until_cursor ()
364         in
365         let rec back_until_cursor () = (* go backward until locked < cursor *)
366           self#_retract ();
367           if (locked_iter ())#compare (cursor_iter ()) > 0 then
368             back_until_cursor ()
369         in
370         let cmp = (locked_iter ())#compare (cursor_iter ()) in
371         (try
372           if cmp < 0 then       (* locked < cursor *)
373             forward_until_cursor ()
374           else if cmp > 0 then  (* locked > cursor *)
375             back_until_cursor ()
376           else                  (* cursor = locked *)
377               ()
378         with Margin -> ())
379
380   method onGoingProof () =
381     match self#status.proof_status with
382     | No_proof | Proof _ -> false
383     | Incomplete_proof _ -> true
384     | Intermediate _ -> assert false
385
386   method proofStatus = MatitaMisc.get_proof_status self#status
387   method proofMetasenv = MatitaMisc.get_proof_metasenv self#status
388   method proofContext = MatitaMisc.get_proof_context self#status
389   method setGoal n = userGoal <- n
390
391   (* debug *)
392   method dump () =
393     MatitaLog.debug "script status:";
394     MatitaLog.debug ("history size: " ^ string_of_int (List.length history));
395     MatitaLog.debug (sprintf "%d statements:" (List.length statements));
396     List.iter MatitaLog.debug statements;
397
398 end
399
400 let _script = ref None
401
402 let script ~buffer ~init ~mathviewer ~urichooser () =
403   let s = new script ~buffer ~init ~mathviewer ~urichooser () in
404   _script := Some s;
405   s
406
407 let instance () = match !_script with None -> assert false | Some s -> s
408
409