]> matita.cs.unibo.it Git - helm.git/blob - helm/software/components/ng_tactics/nTactics.ml
1) mk_meta now returns also the index of the created meta
[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 unify status where a b =
292   let n,h,metasenv,subst,o = status.pstatus in
293   let ctx = match where with `Ctx c -> c | `Term (_,c,_) -> c in
294   let _,_,a = relocate ctx a in
295   let _,_,b = relocate ctx b in
296   let metasenv, subst = 
297     NCicUnification.unify (NCicUnifHint.db ()) metasenv subst ctx a b
298   in
299   { status with pstatus = n,h,metasenv,subst,o }
300 ;;
301
302 let refine status where term expty =
303   let ctx = match where with `Ctx c -> c | `Term (_,c,_) -> c in
304   let nt,_,term = relocate ctx term in
305   let ne, ty = 
306     match expty with None -> None, None 
307     | Some e -> let n,_, e = relocate ctx e in Some n, Some e
308   in
309   let name,height,metasenv,subst,obj = status.pstatus in
310   let db = NCicUnifHint.db () in (* XXX fixme *)
311   let coercion_db = NCicCoercion.db () in
312   let look_for_coercion = NCicCoercion.look_for_coercion coercion_db in
313   let metasenv, subst, t, ty = 
314    NCicRefiner.typeof db ~look_for_coercion metasenv subst ctx term ty
315   in
316   { status with pstatus = (name,height,metasenv,subst,obj) }, 
317   (nt,ctx,t), (ne,ctx,ty)
318 ;;
319
320 let get_goal (status : lowtac_status) (g : int) =
321  let _,_,metasenv,_,_ = status.pstatus in
322  List.assoc g metasenv
323 ;;
324
325 let instantiate status i t =
326  let (goalname, context, _ as ety) = get_goal status i in
327  let status, (_,_,t), (_,_,ty) = refine status (`Term ety) t (Some ety) in
328
329  let name,height,metasenv,subst,obj = status.pstatus in
330  let metasenv = List.filter (fun j,_ -> j <> i) metasenv in
331  let subst = (i, (goalname, context, t, ty)) :: subst in
332  { status with pstatus = (name,height,metasenv,subst,obj) }
333 ;;
334
335 let mk_meta status ?name where bo_or_ty =
336   let n,h,metasenv,subst,o = status.pstatus in
337   let ctx = match where with `Ctx c -> c | `Term (_,c,_) -> c in
338   match bo_or_ty with
339   | `Decl ty ->
340       let _,_,ty = relocate ctx ty in
341       let metasenv, _, instance, _ = 
342         NCicMetaSubst.mk_meta ?name metasenv ctx (`WithType ty)
343       in
344       let status = { status with pstatus = n,h,metasenv,subst,o } in
345       status, (None,ctx,instance)
346   | `Def bo ->
347       let _,_,ty = typeof status (`Ctx ctx) bo in
348       let metasenv, metano, instance, _ = 
349         NCicMetaSubst.mk_meta ?name metasenv ctx (`WithType ty)
350       in
351       let status = { status with pstatus = n,h,metasenv,subst,o } in
352       let status = instantiate status metano bo in
353       status, (None,ctx,instance)
354 ;;
355
356 let select_term low_status (name,context,term) (wanted,path) =
357   let found status ctx t wanted =
358     (* we could lift wanted step-by-step *)
359     try true, unify status (`Ctx ctx) (None,ctx,t) wanted
360     with 
361     | NCicUnification.UnificationFailure _ 
362     | NCicUnification.Uncertain _ -> false, status
363   in
364   let match_term status ctx (wanted : cic_term) t =
365     let rec aux ctx status t =
366       let b, status = found status ctx t wanted in
367       if b then 
368         let status, (_,_,t) = 
369           mk_meta status ~name:in_scope_tag (`Ctx ctx) (`Def (None,ctx,t))
370         in    
371         status, t
372       else NCicUntrusted.map_term_fold_a (fun e c -> e::c) ctx aux status t
373      in 
374        aux ctx status t
375   in 
376   let rec select status ctx pat cic = 
377     match pat, cic with
378     | NCic.LetIn (_,t1,s1,b1), NCic.LetIn (n,t2,s2,b2) ->
379         let status, t = select status ctx t1 t2 in
380         let status, s = select status ctx s1 s2 in
381         let ctx = (n, NCic.Def (s2,t2)) :: ctx in
382         let status, b = select status ctx b1 b2 in
383         status, NCic.LetIn (n,t,s,b)
384     | NCic.Lambda (_,s1,t1), NCic.Lambda (n,s2,t2) ->
385         let status, s = select status ctx s1 s2 in
386         let ctx = (n, NCic.Decl s2) :: ctx in
387         let status, t = select status ctx t1 t2 in
388         status, NCic.Lambda (n,s,t)
389     | NCic.Prod (_,s1,t1), NCic.Prod (n,s2,t2) ->
390         let status, s = select status ctx s1 s2 in
391         let ctx = (n, NCic.Decl s2) :: ctx in
392         let status, t = select status ctx t1 t2 in
393         status, NCic.Prod (n,s,t)
394     | NCic.Appl l1, NCic.Appl l2 ->
395         let status, l = 
396            List.fold_left2
397              (fun (status,l) x y -> 
398               let status, x = select status ctx x y in
399               status, x::l)
400              (status,[]) l1 l2
401         in
402         status, NCic.Appl (List.rev l)
403     | NCic.Match (_,ot1,t1,pl1), NCic.Match (u,ot2,t2,pl2) ->
404         let status, t = select status ctx t1 t2 in
405         let status, ot = select status ctx ot1 ot2 in
406         let status, pl = 
407            List.fold_left2
408              (fun (status,l) x y -> 
409               let status, x = select status ctx x y in
410               status, x::l)
411              (status,[]) pl1 pl2
412         in
413         status, NCic.Match (u,ot,t,List.rev pl)
414     | NCic.Implicit `Hole, t -> 
415         (match wanted with
416         | Some wanted -> 
417              let status, wanted = disambiguate status wanted None (`Ctx ctx) in
418              match_term status ctx wanted t
419         | None -> match_term status ctx (None,ctx,t) t)
420     | NCic.Implicit _, t -> status, t
421     | _,t -> 
422         fail (lazy ("malformed pattern: " ^ NCicPp.ppterm ~metasenv:[]
423           ~context:[] ~subst:[] pat))
424   in
425   let status, term = select low_status context path term in
426   let term = (name, context, term) in
427   mk_meta status ~name:out_scope_tag (`Ctx context) (`Def term)
428 ;;
429
430
431 let exact t status goal =
432  let goalty = get_goal status goal in
433  let status, t = disambiguate status t (Some goalty) (`Term goalty) in
434  instantiate status goal t
435 ;;
436
437
438 let reopen status =
439  let n,h,metasenv,subst,o = status.pstatus in
440  let subst, newm = 
441    List.partition 
442     (function (_,(Some tag,_,_,_)) -> tag <> in_scope_tag && tag <> out_scope_tag
443     | _ -> true)
444     subst 
445  in
446  let in_m, out_m = 
447    List.partition
448      (function (_,(Some tag,_,_,_)) -> tag = in_scope_tag | _ -> assert false)
449      newm
450  in
451  let metasenv = List.map (fun (i,(_,c,_,t)) -> i,(None,c,t)) in_m @ metasenv in
452  let in_m = List.map fst in_m in
453  let out_m = match out_m with [i] -> i | _ -> assert false in
454  { status with pstatus = n,h,metasenv,subst,o }, in_m, out_m 
455 ;;
456
457 let change ~where ~with_what status goal =
458  let (name,_,_ as goalty) = get_goal status goal in
459  let (wanted,_,where) = GrafiteDisambiguate.disambiguate_npattern where in
460  let path = 
461    match where with None -> NCic.Implicit `Term | Some where -> where 
462  in
463  let status, newgoalty = select_term status goalty (wanted,path) in
464  let status, in_scope, out_scope = reopen status in
465  let status =  List.fold_left (exact with_what) status in_scope in
466
467  let j,(n,cctx,bo,_) = out_scope in
468  let _ = typeof status (`Term goalty) (n,cctx,bo)  in
469
470  let n,h,metasenv,subst,o = status.pstatus in
471  let subst = out_scope :: subst in
472  let status = { status with pstatus = n,h,metasenv,subst,o } in
473
474  let status, instance = 
475    mk_meta status ?name (`Term newgoalty) (`Decl newgoalty) 
476  in
477  instantiate status goal instance
478 ;;
479
480 let apply t status goal =
481  let uri,height,metasenv, subst,obj = status.pstatus in
482  let name,context,gty = List.assoc goal metasenv in
483  let metasenv, subst, lexicon_status, t = 
484    GrafiteDisambiguate.disambiguate_nterm (Some gty) 
485     status.lstatus context metasenv subst t 
486  in
487  let subst, metasenv = 
488    (goal, (name, context, t, gty)):: subst,
489    List.filter(fun (x,_) -> x <> goal) metasenv
490  in
491  let new_pstatus = uri,height,metasenv,subst,obj in
492  { lstatus = lexicon_status; pstatus = new_pstatus }
493 ;;
494
495 let apply_tac t = distribute_tac (apply t) ;;
496 let change_tac ~where ~with_what = distribute_tac (change ~where ~with_what) ;;
497