]> matita.cs.unibo.it Git - helm.git/blob - helm/software/components/ng_tactics/nTactics.ml
- select_tac honors the hypotheses pattern when required to generalize them
[helm.git] / helm / software / components / ng_tactics / nTactics.ml
1 (*
2     ||M||  This file is part of HELM, an Hypertextual, Electronic        
3     ||A||  Library of Mathematics, developed at the Computer Science     
4     ||T||  Department, University of Bologna, Italy.                     
5     ||I||                                                                
6     ||T||  HELM is free software; you can redistribute it and/or         
7     ||A||  modify it under the terms of the GNU General Public License   
8     \   /  version 2 or (at your option) any later version.      
9      \ /   This software is distributed as is, NO WARRANTY.     
10       V_______________________________________________________________ *)
11
12 (* $Id: nCic.ml 9058 2008-10-13 17:42:30Z tassi $ *)
13
14 open Printf
15
16 let debug = true
17 let debug_print s = if debug then prerr_endline (Lazy.force s) else ()
18
19 open Continuationals.Stack
20 open NTacStatus
21 module Ast = CicNotationPt
22
23 let dot_tac status =
24   let new_gstatus = 
25     match status.gstatus with
26     | [] -> assert false
27     | ([], _, [], _) :: _ as stack ->
28         (* backward compatibility: do-nothing-dot *)
29         stack
30     | (g, t, k, tag) :: s ->
31         match filter_open g, k with
32         | loc :: loc_tl, _ -> 
33              (([ loc ], t, loc_tl @+ k, tag) :: s) 
34         | [], loc :: k ->
35             assert (is_open loc);
36             (([ loc ], t, k, tag) :: s)
37         | _ -> fail (lazy "can't use \".\" here")
38   in
39    { status with gstatus = new_gstatus }
40 ;;
41
42 let branch_tac status =
43   let new_gstatus = 
44     match status.gstatus with
45     | [] -> assert false
46     | (g, t, k, tag) :: s ->
47           match init_pos g with (* TODO *)
48           | [] | [ _ ] -> fail (lazy "too few goals to branch");
49           | loc :: loc_tl ->
50                ([ loc ], [], [], `BranchTag) :: (loc_tl, t, k, tag) :: s
51   in
52    { status with gstatus = new_gstatus }
53 ;;
54
55 let shift_tac status =
56   let new_gstatus = 
57     match status.gstatus with
58     | (g, t, k, `BranchTag) :: (g', t', k', tag) :: s ->
59           (match g' with
60           | [] -> fail (lazy "no more goals to shift")
61           | loc :: loc_tl ->
62                 (([ loc ], t @+ filter_open g @+ k, [],`BranchTag)
63                 :: (loc_tl, t', k', tag) :: s))
64      | _ -> fail (lazy "can't shift goals here")
65   in
66    { status with gstatus = new_gstatus }
67 ;;
68
69 let pos_tac i_s status =
70   let new_gstatus = 
71     match status.gstatus with
72     | [] -> assert false
73     | ([ loc ], t, [],`BranchTag) :: (g', t', k', tag) :: s
74       when is_fresh loc ->
75         let l_js = List.filter (fun (i, _) -> List.mem i i_s) ([loc] @+ g') in
76           ((l_js, t , [],`BranchTag)
77            :: (([ loc ] @+ g') @- l_js, t', k', tag) :: s)
78     | _ -> fail (lazy "can't use relative positioning here")
79   in
80    { status with gstatus = new_gstatus }
81 ;;
82
83 let wildcard_tac status =
84   let new_gstatus = 
85     match status.gstatus with
86     | [] -> assert false
87     | ([ loc ] , t, [], `BranchTag) :: (g', t', k', tag) :: s
88        when is_fresh loc ->
89             (([loc] @+ g', t, [], `BranchTag) :: ([], t', k', tag) :: s)
90     | _ -> fail (lazy "can't use wildcard here")
91   in
92    { status with gstatus = new_gstatus }
93 ;;
94
95 let merge_tac status =
96   let new_gstatus = 
97     match status.gstatus with
98     | [] -> assert false
99     | (g, t, k,`BranchTag) :: (g', t', k', tag) :: s ->
100         ((t @+ filter_open g @+ g' @+ k, t', k', tag) :: s)
101     | _ -> fail (lazy "can't merge goals here")
102   in
103    { status with gstatus = new_gstatus }
104 ;;
105       
106 let focus_tac gs status =
107   let new_gstatus = 
108     match status.gstatus with
109     | [] -> assert false
110     | s -> assert(gs <> []);
111           let stack_locs =
112             let add_l acc _ _ l = if is_open l then l :: acc else acc in
113             fold ~env:add_l ~cont:add_l ~todo:add_l [] s
114           in
115           List.iter
116             (fun g ->
117               if not (List.exists (fun l -> goal_of_loc l = g) stack_locs) then
118                 fail (lazy (sprintf "goal %d not found (or closed)" g)))
119             gs;
120           (zero_pos gs, [], [], `FocusTag) :: deep_close gs s
121   in
122    { status with gstatus = new_gstatus }
123 ;;
124
125 let unfocus_tac status =
126   let new_gstatus = 
127     match status.gstatus with
128     | [] -> assert false
129     | ([], [], [], `FocusTag) :: s -> s
130     | _ -> fail (lazy "can't unfocus, some goals are still open")
131   in
132    { status with gstatus = new_gstatus }
133 ;;
134
135 let skip_tac status =
136   let new_gstatus = 
137     match status.gstatus with
138     | [] -> assert false
139     | (gl, t, k, tag) :: s -> 
140         let gl = List.map switch_of_loc gl in
141         if List.exists (function Open _ -> true | Closed _ -> false) gl then 
142           fail (lazy "cannot skip an open goal")
143         else 
144           ([],t,k,tag) :: s
145   in
146    { status with gstatus = new_gstatus }
147 ;;
148
149 let block_tac l status =
150   List.fold_left (fun status tac -> tac status) status l
151 ;;
152
153 let compare_statuses ~past ~present =
154  let _,_,past,_,_ = past.pstatus in 
155  let _,_,present,_,_ = present.pstatus in 
156  List.map fst (List.filter (fun (i,_) -> not (List.mem_assoc i past)) present),
157  List.map fst (List.filter (fun (i,_) -> not (List.mem_assoc i present)) past)
158 ;;
159
160
161
162 (* Exec and distribute_tac form a retraction pair:
163     1) exec (distribute_tac low_tac) (s,i) = low_tac (s,i)
164     2) tac [s]::G = G1::...::Gn::G' && G' is G with some goals closed =>
165          distribute_tac (exec tac) [s]::G = (G1@...Gn)::G'
166     3) tac G = distribute_tac (exec tac) G if  
167        tac = distribute_tac lowtac
168
169    Note that executing an high tactic on a set of goals may be stronger
170    than executing the same tactic on those goals, but once at a time
171    (e.g. the tactic could perform a global analysis of the set of goals)
172 *)
173
174 let exec tac low_status g =
175   let stack = [ [0,Open g], [], [], `NoTag ] in
176   let status = tac { gstatus = stack ; istatus = low_status } in
177    status.istatus
178 ;;
179
180 let distribute_tac tac status =
181   match status.gstatus with
182   | [] -> assert false
183   | (g, t, k, tag) :: s ->
184       debug_print (lazy ("context length " ^string_of_int (List.length g)));
185       let rec aux s go gc =
186         function
187         | [] -> s, go, gc
188         | loc :: loc_tl ->
189             debug_print (lazy "inner eval tactical");
190             let s, go, gc =
191               if List.exists ((=) (goal_of_loc loc)) gc then
192                 s, go, gc
193               else
194                 match switch_of_loc loc with
195                 | Closed _ -> fail (lazy "cannot apply to a Closed goal")
196                 | Open n -> 
197                    let sn = tac s n in
198                    let go', gc' = compare_statuses ~past:s ~present:sn in
199                    sn, (go @- gc') @+ go', gc @+ gc'
200             in
201             aux s go gc loc_tl
202       in
203       let s0, go0, gc0 = status.istatus, [], [] in
204       let sn, gon, gcn = aux s0 go0 gc0 g in
205       debug_print (lazy ("opened: "
206         ^ String.concat " " (List.map string_of_int gon)));
207       debug_print (lazy ("closed: "
208         ^ String.concat " " (List.map string_of_int gcn)));
209       let stack =
210         (zero_pos gon, t @~- gcn, k @~- gcn, tag) :: deep_close gcn s
211       in
212        { gstatus = stack; istatus = sn }
213 ;;
214
215 let exact t status goal =
216  let goalty = get_goalty status goal in
217  let status, t = disambiguate status t (Some goalty) (ctx_of goalty) in
218  instantiate status goal t
219 ;;
220
221 let exact_tac t = distribute_tac (exact t) ;;
222
223 let find_in_context name context =
224   let rec aux acc = function
225     | [] -> raise Not_found
226     | (hd,_) :: tl when hd = name -> acc
227     | _ :: tl ->  aux (acc + 1) tl
228   in
229   aux 1 context
230 ;;
231
232 let clear names status goal =
233  let goalty = get_goalty status goal in
234  let js =
235    List.map 
236      (fun name -> 
237         try find_in_context name (ctx_of goalty)
238         with Not_found -> 
239           fail (lazy ("hypothesis '" ^ name ^ "' not found"))) 
240      names
241  in
242  let n,h,metasenv,subst,o = status.pstatus in
243  let metasenv,subst,_ = NCicMetaSubst.restrict metasenv subst goal js in
244   { status with pstatus = n,h,metasenv,subst,o }
245 ;;
246
247 let clear_tac names = distribute_tac (clear names);;
248
249 let select ~where:(wanted,_,where) status goal = 
250  let goalty = get_goalty status goal in
251  let path = 
252    match where with None -> NCic.Implicit `Term | Some where -> where 
253  in
254  let status, newgoalty = select_term status goalty (wanted,path) in
255  let status, instance = 
256    mk_meta status (ctx_of newgoalty) (`Decl newgoalty) 
257  in
258  instantiate status goal instance
259 ;;
260
261 let select_tac ~where = distribute_tac (select ~where) ;;
262
263 let select_tac ~where move_down_hyps = 
264  let (wanted,hyps,where) = GrafiteDisambiguate.disambiguate_npattern where in
265  let path = 
266   match where with None -> NCic.Implicit `Term | Some where -> where in
267  if not move_down_hyps then
268   select_tac ~where:(wanted,hyps,Some path)
269  else
270   let path = 
271    List.fold_left
272      (fun path (name,path_name) -> NCic.Prod ("_",path_name,path))
273      path (List.rev hyps)
274   in
275    let generalize_tac =
276     distribute_tac
277      (exec (exact_tac 
278          ("",0,Ast.Appl 
279            (Ast.Implicit :: List.map (fun (name,_) -> Ast.Ident (name,None)) 
280              hyps))))
281    in
282    block_tac [ 
283      generalize_tac; 
284      select_tac ~where:(wanted,[],Some path);
285      clear_tac (List.map fst hyps) ]
286 ;;
287
288 let reopen status =
289  let n,h,metasenv,subst,o = status.pstatus in
290  let subst, newm = 
291    List.partition 
292     (function (_,(Some tag,_,_,_)) -> 
293             tag <> NCicMetaSubst.in_scope_tag && 
294             not (NCicMetaSubst.is_out_scope_tag tag)
295     | _ -> true)
296     subst 
297  in
298  let in_m, out_m = 
299    List.partition
300      (function (_,(Some tag,_,_,_)) -> 
301          tag = NCicMetaSubst.in_scope_tag | _ -> assert false)
302      newm
303  in
304  let metasenv = List.map (fun (i,(_,c,_,t)) -> i,(None,c,t)) in_m @ metasenv in
305  let in_m = List.map fst in_m in
306  let out_m = match out_m with [i] -> i | _ -> assert false in
307  { status with pstatus = n,h,metasenv,subst,o }, in_m, out_m 
308 ;;
309
310 let change ~where ~with_what status goal =
311  let goalty = get_goalty status goal in
312  let (wanted,_,where) = GrafiteDisambiguate.disambiguate_npattern where in
313  let path = 
314    match where with None -> NCic.Implicit `Term | Some where -> where 
315  in
316  let status, newgoalty = select_term status goalty (wanted,path) in
317  let status, in_scope, out_scope = reopen status in
318  let status =  List.fold_left (exact with_what) status in_scope in
319
320  let j,(n,cctx,bo,_) = out_scope in
321  let _ = typeof status (ctx_of goalty) (Obj.magic (n,cctx,bo))  in
322
323  let n,h,metasenv,subst,o = status.pstatus in
324  let subst = out_scope :: subst in
325  let status = { status with pstatus = n,h,metasenv,subst,o } in
326
327  let status, instance = 
328    mk_meta status (ctx_of newgoalty) (`Decl newgoalty) 
329  in
330  instantiate status goal instance
331 ;;
332
333 let apply t status goal = exact t status goal;;
334
335 let apply_tac t = distribute_tac (apply t) ;;
336 let change_tac ~where ~with_what = distribute_tac (change ~where ~with_what) ;;
337
338 let elim_tac ~what ~where status =
339  block_tac
340    [ select_tac ~where true;
341      distribute_tac (fun status goal ->
342        let goalty = get_goalty status goal in
343        let _,_,w = what in
344        let status, what = 
345          disambiguate status what None (ctx_of goalty) in
346        let _ty_what = typeof status (ctx_of what) what in 
347        (* check inductive... find eliminator *)
348        let holes = [ 
349          Ast.Implicit;Ast.Implicit;Ast.Implicit]
350        in
351        let eliminator = 
352          Ast.Appl(Ast.Ident("nat_ind",None)::holes @ [ w ])
353        in
354        exec (apply_tac ("",0,eliminator)) status goal) ] 
355    status
356 ;;
357
358 let intro_tac name =
359  block_tac
360   [ exact_tac
361     ("",0,(Ast.Binder (`Lambda,
362      (Ast.Ident (name,None),None),Ast.Implicit)));
363      if name = "_" then clear_tac [name] else fun x -> x ]
364 ;;
365
366 let cases ~what status goal =
367  let gty = get_goalty status goal in
368  let status, what = disambiguate status what None (ctx_of gty) in
369  let ty = typeof status (ctx_of what) what in
370  let ref, consno, left, right = analyse_indty status ty in
371  let t =
372   NCic.Match (ref,NCic.Implicit `Term, term_of_cic_term what (ctx_of gty),
373     HExtlib.mk_list (NCic.Implicit `Term) consno)
374  in
375  let ctx = ctx_of gty in
376  let status,t,ty = refine status ctx (mk_cic_term ctx t) (Some gty) in
377  instantiate status goal t
378 ;;
379
380 let cases_tac ~what ~where = 
381         block_tac [ select_tac ~where true ; distribute_tac (cases ~what) ]
382 ;;
383
384 let case1_tac name =
385  let name = if name = "_" then "_clearme" else name in
386  block_tac [ intro_tac name; 
387              cases_tac 
388               ~where:("",0,(None,[],None)) 
389               ~what:("",0,Ast.Ident (name,None));
390               if name = "_clearme" then clear_tac ["_clearme"] else fun x -> x ]
391 ;;