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