]> matita.cs.unibo.it Git - helm.git/blob - helm/software/components/ng_tactics/nTactics.ml
New tactic "case1_tac" that make "intro" followed by case of the first
[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 exception Error of string lazy_t
20 let fail msg = raise (Error msg)
21
22 type lowtac_status = {
23         pstatus : NCic.obj;
24         lstatus : LexiconEngine.status
25 }
26
27 type lowtactic = lowtac_status -> int -> lowtac_status 
28
29 type tac_status = {
30         gstatus : Continuationals.Stack.t; 
31         istatus : lowtac_status;
32
33
34 type tactic = tac_status -> tac_status
35
36 type tactic_term = CicNotationPt.term Disambiguate.disambiguator_input
37 type tactic_pattern = GrafiteAst.npattern Disambiguate.disambiguator_input
38
39 let pp_tac_status status = 
40   prerr_endline (NCicPp.ppobj status.istatus.pstatus)
41 ;;
42
43 let pp_lowtac_status status = 
44   prerr_endline "--------------------------------------------";
45   prerr_endline (NCicPp.ppobj status.pstatus)
46 ;;
47
48 open Continuationals.Stack
49
50 let dot_tac status =
51   let new_gstatus = 
52     match status.gstatus with
53     | [] -> assert false
54     | ([], _, [], _) :: _ as stack ->
55         (* backward compatibility: do-nothing-dot *)
56         stack
57     | (g, t, k, tag) :: s ->
58         match filter_open g, k with
59         | loc :: loc_tl, _ -> 
60              (([ loc ], t, loc_tl @+ k, tag) :: s) 
61         | [], loc :: k ->
62             assert (is_open loc);
63             (([ loc ], t, k, tag) :: s)
64         | _ -> fail (lazy "can't use \".\" here")
65   in
66    { status with gstatus = new_gstatus }
67 ;;
68
69 let branch_tac status =
70   let new_gstatus = 
71     match status.gstatus with
72     | [] -> assert false
73     | (g, t, k, tag) :: s ->
74           match init_pos g with (* TODO *)
75           | [] | [ _ ] -> fail (lazy "too few goals to branch");
76           | loc :: loc_tl ->
77                ([ loc ], [], [], `BranchTag) :: (loc_tl, t, k, tag) :: s
78   in
79    { status with gstatus = new_gstatus }
80 ;;
81
82 let shift_tac status =
83   let new_gstatus = 
84     match status.gstatus with
85     | (g, t, k, `BranchTag) :: (g', t', k', tag) :: s ->
86           (match g' with
87           | [] -> fail (lazy "no more goals to shift")
88           | loc :: loc_tl ->
89                 (([ loc ], t @+ filter_open g @+ k, [],`BranchTag)
90                 :: (loc_tl, t', k', tag) :: s))
91      | _ -> fail (lazy "can't shift goals here")
92   in
93    { status with gstatus = new_gstatus }
94 ;;
95
96 let pos_tac i_s status =
97   let new_gstatus = 
98     match status.gstatus with
99     | [] -> assert false
100     | ([ loc ], t, [],`BranchTag) :: (g', t', k', tag) :: s
101       when is_fresh loc ->
102         let l_js = List.filter (fun (i, _) -> List.mem i i_s) ([loc] @+ g') in
103           ((l_js, t , [],`BranchTag)
104            :: (([ loc ] @+ g') @- l_js, t', k', tag) :: s)
105     | _ -> fail (lazy "can't use relative positioning here")
106   in
107    { status with gstatus = new_gstatus }
108 ;;
109
110 let wildcard_tac status =
111   let new_gstatus = 
112     match status.gstatus with
113     | [] -> assert false
114     | ([ loc ] , t, [], `BranchTag) :: (g', t', k', tag) :: s
115        when is_fresh loc ->
116             (([loc] @+ g', t, [], `BranchTag) :: ([], t', k', tag) :: s)
117     | _ -> fail (lazy "can't use wildcard here")
118   in
119    { status with gstatus = new_gstatus }
120 ;;
121
122 let merge_tac status =
123   let new_gstatus = 
124     match status.gstatus with
125     | [] -> assert false
126     | (g, t, k,`BranchTag) :: (g', t', k', tag) :: s ->
127         ((t @+ filter_open g @+ g' @+ k, t', k', tag) :: s)
128     | _ -> fail (lazy "can't merge goals here")
129   in
130    { status with gstatus = new_gstatus }
131 ;;
132       
133 let focus_tac gs status =
134   let new_gstatus = 
135     match status.gstatus with
136     | [] -> assert false
137     | s -> assert(gs <> []);
138           let stack_locs =
139             let add_l acc _ _ l = if is_open l then l :: acc else acc in
140             fold ~env:add_l ~cont:add_l ~todo:add_l [] s
141           in
142           List.iter
143             (fun g ->
144               if not (List.exists (fun l -> goal_of_loc l = g) stack_locs) then
145                 fail (lazy (sprintf "goal %d not found (or closed)" g)))
146             gs;
147           (zero_pos gs, [], [], `FocusTag) :: deep_close gs s
148   in
149    { status with gstatus = new_gstatus }
150 ;;
151
152 let unfocus_tac status =
153   let new_gstatus = 
154     match status.gstatus with
155     | [] -> assert false
156     | ([], [], [], `FocusTag) :: s -> s
157     | _ -> fail (lazy "can't unfocus, some goals are still open")
158   in
159    { status with gstatus = new_gstatus }
160 ;;
161
162 let skip_tac status =
163   let new_gstatus = 
164     match status.gstatus with
165     | [] -> assert false
166     | (gl, t, k, tag) :: s -> 
167         let gl = List.map switch_of_loc gl in
168         if List.exists (function Open _ -> true | Closed _ -> false) gl then 
169           fail (lazy "cannot skip an open goal")
170         else 
171           ([],t,k,tag) :: s
172   in
173    { status with gstatus = new_gstatus }
174 ;;
175
176 let block_tac l status =
177   List.fold_left (fun status tac -> tac status) status l
178 ;;
179
180 let compare_statuses ~past ~present =
181  let _,_,past,_,_ = past.pstatus in 
182  let _,_,present,_,_ = present.pstatus in 
183  List.map fst (List.filter (fun (i,_) -> not (List.mem_assoc i past)) present),
184  List.map fst (List.filter (fun (i,_) -> not (List.mem_assoc i present)) past)
185 ;;
186
187
188
189 (* Exec and distribute_tac form a retraction pair:
190     1) exec (distribute_tac low_tac) (s,i) = low_tac (s,i)
191     2) tac [s]::G = G1::...::Gn::G' && G' is G with some goals closed =>
192          distribute_tac (exec tac) [s]::G = (G1@...Gn)::G'
193     3) tac G = distribute_tac (exec tac) G if  
194        tac = distribute_tac lowtac
195
196    Note that executing an high tactic on a set of goals may be stronger
197    than executing the same tactic on those goals, but once at a time
198    (e.g. the tactic could perform a global analysis of the set of goals)
199 *)
200
201 let exec tac low_status g =
202   let stack = [ [0,Open g], [], [], `NoTag ] in
203   let status = tac { gstatus = stack ; istatus = low_status } in
204    status.istatus
205 ;;
206
207 let distribute_tac tac status =
208   match status.gstatus with
209   | [] -> assert false
210   | (g, t, k, tag) :: s ->
211       debug_print (lazy ("context length " ^string_of_int (List.length g)));
212       let rec aux s go gc =
213         function
214         | [] -> s, go, gc
215         | loc :: loc_tl ->
216             debug_print (lazy "inner eval tactical");
217             let s, go, gc =
218               if List.exists ((=) (goal_of_loc loc)) gc then
219                 s, go, gc
220               else
221                 match switch_of_loc loc with
222                 | Closed _ -> fail (lazy "cannot apply to a Closed goal")
223                 | Open n -> 
224                    let sn = tac s n in
225                    let go', gc' = compare_statuses ~past:s ~present:sn in
226                    sn, (go @- gc') @+ go', gc @+ gc'
227             in
228             aux s go gc loc_tl
229       in
230       let s0, go0, gc0 = status.istatus, [], [] in
231       let sn, gon, gcn = aux s0 go0 gc0 g in
232       debug_print (lazy ("opened: "
233         ^ String.concat " " (List.map string_of_int gon)));
234       debug_print (lazy ("closed: "
235         ^ String.concat " " (List.map string_of_int gcn)));
236       let stack =
237         (zero_pos gon, t @~- gcn, k @~- gcn, tag) :: deep_close gcn s
238       in
239        { gstatus = stack; istatus = sn }
240 ;;
241
242 type cic_term = NCic.conjecture
243 type ast_term = string * int * CicNotationPt.term
244 type position = [ `Ctx of NCic.context | `Term of cic_term ]
245
246
247 let relocate context (name,ctx,t as term) =
248   let is_prefix l1 l2 =
249     let rec aux = function
250       | [],[] -> true
251       | x::xs, y::ys -> x=y && aux (xs,ys)
252       | _ -> false
253     in
254       aux (List.rev l1, List.rev l2)
255   in
256   if ctx = context then term else 
257   if is_prefix ctx context then 
258     (name, context, 
259       NCicSubstitution.lift (List.length context - List.length ctx) t)
260   else
261     assert false
262 ;;
263
264 let disambiguate (status : lowtac_status) (t : ast_term)  
265                  (ty : cic_term option) (where : position) =
266  let uri,height,metasenv,subst,obj = status.pstatus in
267  let context = match where with `Ctx c -> c | `Term (_,c,_) -> c in
268  let expty = 
269    match ty with 
270    | None -> None | Some ty -> let _,_,x = relocate context ty in Some x 
271  in
272  let metasenv, subst, lexicon_status, t = 
273    GrafiteDisambiguate.disambiguate_nterm expty
274     status.lstatus context metasenv subst t 
275  in
276  let new_pstatus = uri,height,metasenv,subst,obj in
277  { lstatus = lexicon_status; pstatus = new_pstatus }, (None, context, t) 
278 ;;
279
280 let in_scope_tag = "tag:in_scope" ;;
281 let out_scope_tag = "tag:out_scope" ;;
282
283 let typeof status where t =
284   let _,_,metasenv,subst,_ = status.pstatus in
285   let ctx = match where with `Ctx c -> c | `Term (_,c,_) -> c in
286   let _,_,t = relocate ctx t in
287   let ty = NCicTypeChecker.typeof ~subst ~metasenv ctx t in
288   None, ctx, ty
289 ;;
290   
291 let whd status ?delta where t =
292   let _,_,metasenv,subst,_ = status.pstatus in
293   let ctx = match where with `Ctx c -> c | `Term (_,c,_) -> c in
294   let _,_,t = relocate ctx t in
295   let t = NCicReduction.whd ~subst ?delta ctx t in
296   None, ctx, t
297 ;;
298   
299 let unify status where a b =
300   let n,h,metasenv,subst,o = status.pstatus in
301   let ctx = match where with `Ctx c -> c | `Term (_,c,_) -> c in
302   let _,_,a = relocate ctx a in
303   let _,_,b = relocate ctx b in
304   let metasenv, subst = 
305     NCicUnification.unify (NCicUnifHint.db ()) metasenv subst ctx a b
306   in
307   { status with pstatus = n,h,metasenv,subst,o }
308 ;;
309
310 let refine status where term expty =
311   let ctx = match where with `Ctx c -> c | `Term (_,c,_) -> c in
312   let nt,_,term = relocate ctx term in
313   let ne, ty = 
314     match expty with None -> None, None 
315     | Some e -> let n,_, e = relocate ctx e in Some n, Some e
316   in
317   let name,height,metasenv,subst,obj = status.pstatus in
318   let db = NCicUnifHint.db () in (* XXX fixme *)
319   let coercion_db = NCicCoercion.db () in
320   let look_for_coercion = NCicCoercion.look_for_coercion coercion_db in
321   let metasenv, subst, t, ty = 
322    NCicRefiner.typeof db ~look_for_coercion metasenv subst ctx term ty
323   in
324   { status with pstatus = (name,height,metasenv,subst,obj) }, 
325   (nt,ctx,t), (ne,ctx,ty)
326 ;;
327
328 let get_goal (status : lowtac_status) (g : int) =
329  let _,_,metasenv,_,_ = status.pstatus in
330  List.assoc g metasenv
331 ;;
332
333 let instantiate status i t =
334  let (goalname, context, _ as ety) = get_goal status i in
335  let status, (_,_,t), (_,_,ty) = refine status (`Term ety) t (Some ety) in
336
337  let name,height,metasenv,subst,obj = status.pstatus in
338  let metasenv = List.filter (fun j,_ -> j <> i) metasenv in
339  let subst = (i, (goalname, context, t, ty)) :: subst in
340  { status with pstatus = (name,height,metasenv,subst,obj) }
341 ;;
342
343 let mk_meta status ?name where bo_or_ty =
344   let n,h,metasenv,subst,o = status.pstatus in
345   let ctx = match where with `Ctx c -> c | `Term (_,c,_) -> c in
346   match bo_or_ty with
347   | `Decl ty ->
348       let _,_,ty = relocate ctx ty in
349       let metasenv, _, instance, _ = 
350         NCicMetaSubst.mk_meta ?name metasenv ctx (`WithType ty)
351       in
352       let status = { status with pstatus = n,h,metasenv,subst,o } in
353       status, (None,ctx,instance)
354   | `Def bo ->
355       let _,_,ty = typeof status (`Ctx ctx) bo in
356       let metasenv, metano, instance, _ = 
357         NCicMetaSubst.mk_meta ?name metasenv ctx (`WithType ty)
358       in
359       let status = { status with pstatus = n,h,metasenv,subst,o } in
360       let status = instantiate status metano bo in
361       status, (None,ctx,instance)
362 ;;
363
364 let select_term low_status (name,context,term) (wanted,path) =
365   let found status ctx t wanted =
366     (* we could lift wanted step-by-step *)
367     try true, unify status (`Ctx ctx) (None,ctx,t) wanted
368     with 
369     | NCicUnification.UnificationFailure _ 
370     | NCicUnification.Uncertain _ -> false, status
371   in
372   let match_term status ctx (wanted : cic_term) t =
373     let rec aux ctx status t =
374       let b, status = found status ctx t wanted in
375       if b then 
376         let status, (_,_,t) = 
377           mk_meta status ~name:in_scope_tag (`Ctx ctx) (`Def (None,ctx,t))
378         in    
379         status, t
380       else NCicUntrusted.map_term_fold_a (fun e c -> e::c) ctx aux status t
381      in 
382        aux ctx status t
383   in 
384   let rec select status ctx pat cic = 
385     match pat, cic with
386     | NCic.LetIn (_,t1,s1,b1), NCic.LetIn (n,t2,s2,b2) ->
387         let status, t = select status ctx t1 t2 in
388         let status, s = select status ctx s1 s2 in
389         let ctx = (n, NCic.Def (s2,t2)) :: ctx in
390         let status, b = select status ctx b1 b2 in
391         status, NCic.LetIn (n,t,s,b)
392     | NCic.Lambda (_,s1,t1), NCic.Lambda (n,s2,t2) ->
393         let status, s = select status ctx s1 s2 in
394         let ctx = (n, NCic.Decl s2) :: ctx in
395         let status, t = select status ctx t1 t2 in
396         status, NCic.Lambda (n,s,t)
397     | NCic.Prod (_,s1,t1), NCic.Prod (n,s2,t2) ->
398         let status, s = select status ctx s1 s2 in
399         let ctx = (n, NCic.Decl s2) :: ctx in
400         let status, t = select status ctx t1 t2 in
401         status, NCic.Prod (n,s,t)
402     | NCic.Appl l1, NCic.Appl l2 ->
403         let status, l = 
404            List.fold_left2
405              (fun (status,l) x y -> 
406               let status, x = select status ctx x y in
407               status, x::l)
408              (status,[]) l1 l2
409         in
410         status, NCic.Appl (List.rev l)
411     | NCic.Match (_,ot1,t1,pl1), NCic.Match (u,ot2,t2,pl2) ->
412         let status, t = select status ctx t1 t2 in
413         let status, ot = select status ctx ot1 ot2 in
414         let status, pl = 
415            List.fold_left2
416              (fun (status,l) x y -> 
417               let status, x = select status ctx x y in
418               status, x::l)
419              (status,[]) pl1 pl2
420         in
421         status, NCic.Match (u,ot,t,List.rev pl)
422     | NCic.Implicit `Hole, t -> 
423         (match wanted with
424         | Some wanted -> 
425              let status, wanted = disambiguate status wanted None (`Ctx ctx) in
426              match_term status ctx wanted t
427         | None -> match_term status ctx (None,ctx,t) t)
428     | NCic.Implicit _, t -> status, t
429     | _,t -> 
430         fail (lazy ("malformed pattern: " ^ NCicPp.ppterm ~metasenv:[]
431           ~context:[] ~subst:[] pat))
432   in
433   let status, term = select low_status context path term in
434   let term = (name, context, term) in
435   mk_meta status ~name:out_scope_tag (`Ctx context) (`Def term)
436 ;;
437
438 let select ~where status goal = 
439  let name, _, _ as goalty = get_goal status goal in
440  let (wanted,_,where) = GrafiteDisambiguate.disambiguate_npattern where in
441  let path = 
442    match where with None -> NCic.Implicit `Term | Some where -> where 
443  in
444  let status, newgoalty = select_term status goalty (wanted,path) in
445  let status, instance = 
446    mk_meta status ?name (`Term newgoalty) (`Decl newgoalty) 
447  in
448  instantiate status goal instance
449 ;;
450
451 let select_tac ~where = distribute_tac (select ~where) ;;
452
453 let exact t status goal =
454  let goalty = get_goal status goal in
455  let status, t = disambiguate status t (Some goalty) (`Term goalty) in
456  instantiate status goal t
457 ;;
458
459 let exact_tac t = distribute_tac (exact t) ;;
460
461 let reopen status =
462  let n,h,metasenv,subst,o = status.pstatus in
463  let subst, newm = 
464    List.partition 
465     (function (_,(Some tag,_,_,_)) -> tag <> in_scope_tag && tag <> out_scope_tag
466     | _ -> true)
467     subst 
468  in
469  let in_m, out_m = 
470    List.partition
471      (function (_,(Some tag,_,_,_)) -> tag = in_scope_tag | _ -> assert false)
472      newm
473  in
474  let metasenv = List.map (fun (i,(_,c,_,t)) -> i,(None,c,t)) in_m @ metasenv in
475  let in_m = List.map fst in_m in
476  let out_m = match out_m with [i] -> i | _ -> assert false in
477  { status with pstatus = n,h,metasenv,subst,o }, in_m, out_m 
478 ;;
479
480 let change ~where ~with_what status goal =
481  let (name,_,_ as goalty) = get_goal status goal in
482  let (wanted,_,where) = GrafiteDisambiguate.disambiguate_npattern where in
483  let path = 
484    match where with None -> NCic.Implicit `Term | Some where -> where 
485  in
486  let status, newgoalty = select_term status goalty (wanted,path) in
487  let status, in_scope, out_scope = reopen status in
488  let status =  List.fold_left (exact with_what) status in_scope in
489
490  let j,(n,cctx,bo,_) = out_scope in
491  let _ = typeof status (`Term goalty) (n,cctx,bo)  in
492
493  let n,h,metasenv,subst,o = status.pstatus in
494  let subst = out_scope :: subst in
495  let status = { status with pstatus = n,h,metasenv,subst,o } in
496
497  let status, instance = 
498    mk_meta status ?name (`Term newgoalty) (`Decl newgoalty) 
499  in
500  instantiate status goal instance
501 ;;
502
503 let apply t status goal = exact t status goal;;
504
505 let apply_tac t = distribute_tac (apply t) ;;
506 let change_tac ~where ~with_what = distribute_tac (change ~where ~with_what) ;;
507
508 let elim_tac ~what ~where status =
509  block_tac
510    [ select_tac ~where;
511      distribute_tac (fun status goal ->
512        let goalty = get_goal status goal in
513        let status, (_,_,w as what) = 
514          disambiguate status what None (`Term goalty) in
515        let _ty_what = typeof status (`Term what) what in 
516        (* check inductive... find eliminator *)
517        let w = (*astify what *) CicNotationPt.Ident ("m",None) in
518        let holes = [ 
519          CicNotationPt.Implicit;CicNotationPt.Implicit;CicNotationPt.Implicit]
520        in
521        let eliminator = 
522          CicNotationPt.Appl(CicNotationPt.Ident("nat_ind",None)::holes @ [ w ])
523        in
524        exec (apply_tac ("",0,eliminator)) status goal) ] 
525    status
526 ;;
527
528 let intro_tac name =
529  exact_tac
530   ("",0,(CicNotationPt.Binder (`Lambda,
531    (CicNotationPt.Ident (name,None),None),CicNotationPt.Implicit)))
532 ;;
533
534 let case status goal =
535  let _,ctx,_ = get_goal status goal in
536  let ty = typeof status (`Ctx ctx) ("",ctx,NCic.Rel 1) in
537  let ref =
538   match whd status (`Ctx ctx) ty with
539      _,_,NCic.Const ref
540    | _,_,NCic.Appl (NCic.Const ref :: _) -> ref
541    | _,_,_ -> fail (lazy ("not an inductive type")) in
542  let _,_,tl,_,i = NCicEnvironment.get_checked_indtys ref in
543  let _,_,_,cl = List.nth tl i in
544  let consno = List.length cl in
545  let t =
546   NCic.Match (ref,NCic.Implicit `Term,NCic.Rel 1,HExtlib.mk_list (NCic.Implicit `Term) consno)
547  in
548  let status,t,ty = refine status (`Ctx ctx) ("",ctx,t) None in
549  instantiate status goal t
550 ;;
551
552 let case_tac = distribute_tac case;;
553
554 let case1_tac name =
555  block_tac [ intro_tac name; case_tac ]
556 ;;