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.
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_______________________________________________________________ *)
12 (* $Id: nCic.ml 9058 2008-10-13 17:42:30Z tassi $ *)
17 let debug_print s = if debug then prerr_endline (Lazy.force s) else ()
19 exception Error of string lazy_t
20 let fail msg = raise (Error msg)
22 type lowtac_status = {
24 lstatus : LexiconEngine.status
27 type lowtactic = lowtac_status -> int -> lowtac_status
30 gstatus : Continuationals.Stack.t;
31 istatus : lowtac_status;
34 type tactic = tac_status -> tac_status
36 type tactic_term = CicNotationPt.term Disambiguate.disambiguator_input
37 type tactic_pattern = GrafiteAst.npattern Disambiguate.disambiguator_input
39 let pp_tac_status status =
40 prerr_endline (NCicPp.ppobj status.istatus.pstatus)
43 let pp_lowtac_status status =
44 prerr_endline "--------------------------------------------";
45 prerr_endline (NCicPp.ppobj status.pstatus)
48 open Continuationals.Stack
52 match status.gstatus with
54 | ([], _, [], _) :: _ as stack ->
55 (* backward compatibility: do-nothing-dot *)
57 | (g, t, k, tag) :: s ->
58 match filter_open g, k with
60 (([ loc ], t, loc_tl @+ k, tag) :: s)
63 (([ loc ], t, k, tag) :: s)
64 | _ -> fail (lazy "can't use \".\" here")
66 { status with gstatus = new_gstatus }
69 let branch_tac status =
71 match status.gstatus with
73 | (g, t, k, tag) :: s ->
74 match init_pos g with (* TODO *)
75 | [] | [ _ ] -> fail (lazy "too few goals to branch");
77 ([ loc ], [], [], `BranchTag) :: (loc_tl, t, k, tag) :: s
79 { status with gstatus = new_gstatus }
82 let shift_tac status =
84 match status.gstatus with
85 | (g, t, k, `BranchTag) :: (g', t', k', tag) :: s ->
87 | [] -> fail (lazy "no more goals to shift")
89 (([ loc ], t @+ filter_open g @+ k, [],`BranchTag)
90 :: (loc_tl, t', k', tag) :: s))
91 | _ -> fail (lazy "can't shift goals here")
93 { status with gstatus = new_gstatus }
96 let pos_tac i_s status =
98 match status.gstatus with
100 | ([ loc ], t, [],`BranchTag) :: (g', t', k', tag) :: s
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")
107 { status with gstatus = new_gstatus }
110 let wildcard_tac status =
112 match status.gstatus with
114 | ([ loc ] , t, [], `BranchTag) :: (g', t', k', tag) :: s
116 (([loc] @+ g', t, [], `BranchTag) :: ([], t', k', tag) :: s)
117 | _ -> fail (lazy "can't use wildcard here")
119 { status with gstatus = new_gstatus }
122 let merge_tac status =
124 match status.gstatus with
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")
130 { status with gstatus = new_gstatus }
133 let focus_tac gs status =
135 match status.gstatus with
137 | s -> assert(gs <> []);
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
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)))
147 (zero_pos gs, [], [], `FocusTag) :: deep_close gs s
149 { status with gstatus = new_gstatus }
152 let unfocus_tac status =
154 match status.gstatus with
156 | ([], [], [], `FocusTag) :: s -> s
157 | _ -> fail (lazy "can't unfocus, some goals are still open")
159 { status with gstatus = new_gstatus }
162 let skip_tac status =
164 match status.gstatus with
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")
173 { status with gstatus = new_gstatus }
176 let block_tac l status =
177 List.fold_left (fun status tac -> tac status) status l
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)
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
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)
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
207 let distribute_tac tac status =
208 match status.gstatus with
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 =
216 debug_print (lazy "inner eval tactical");
218 if List.exists ((=) (goal_of_loc loc)) gc then
221 match switch_of_loc loc with
222 | Closed _ -> fail (lazy "cannot apply to a Closed goal")
225 let go', gc' = compare_statuses ~past:s ~present:sn in
226 sn, (go @- gc') @+ go', gc @+ gc'
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)));
237 (zero_pos gon, t @~- gcn, k @~- gcn, tag) :: deep_close gcn s
239 { gstatus = stack; istatus = sn }
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 ]
247 let relocate context (name,ctx,t as term) =
248 let is_prefix l1 l2 =
249 let rec aux = function
251 | x::xs, y::ys -> x=y && aux (xs,ys)
254 aux (List.rev l1, List.rev l2)
256 if ctx = context then term else
257 if is_prefix ctx context then
259 NCicSubstitution.lift (List.length context - List.length ctx) t)
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
270 | None -> None | Some ty -> let _,_,x = relocate context ty in Some x
272 let metasenv, subst, lexicon_status, t =
273 GrafiteDisambiguate.disambiguate_nterm expty
274 status.lstatus context metasenv subst t
276 let new_pstatus = uri,height,metasenv,subst,obj in
277 { lstatus = lexicon_status; pstatus = new_pstatus }, (None, context, t)
280 let in_scope_tag = "tag:in_scope" ;;
281 let out_scope_tag = "tag:out_scope" ;;
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
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
299 { status with pstatus = n,h,metasenv,subst,o }
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
306 match expty with None -> None, None
307 | Some e -> let n,_, e = relocate ctx e in Some n, Some e
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
316 { status with pstatus = (name,height,metasenv,subst,obj) },
317 (nt,ctx,t), (ne,ctx,ty)
320 let get_goal (status : lowtac_status) (g : int) =
321 let _,_,metasenv,_,_ = status.pstatus in
322 List.assoc g metasenv
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
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) }
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
340 let _,_,ty = relocate ctx ty in
341 let metasenv, _, instance, _ =
342 NCicMetaSubst.mk_meta ?name metasenv ctx (`WithType ty)
344 let status = { status with pstatus = n,h,metasenv,subst,o } in
345 status, (None,ctx,instance)
347 let _,_,ty = typeof status (`Ctx ctx) bo in
348 let metasenv, metano, instance, _ =
349 NCicMetaSubst.mk_meta ?name metasenv ctx (`WithType ty)
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)
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
361 | NCicUnification.UnificationFailure _
362 | NCicUnification.Uncertain _ -> false, status
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
368 let status, (_,_,t) =
369 mk_meta status ~name:in_scope_tag (`Ctx ctx) (`Def (None,ctx,t))
372 else NCicUntrusted.map_term_fold_a (fun e c -> e::c) ctx aux status t
376 let rec select status ctx pat cic =
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 ->
397 (fun (status,l) x y ->
398 let status, x = select status ctx x y 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
408 (fun (status,l) x y ->
409 let status, x = select status ctx x y in
413 status, NCic.Match (u,ot,t,List.rev pl)
414 | NCic.Implicit `Hole, t ->
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
422 fail (lazy ("malformed pattern: " ^ NCicPp.ppterm ~metasenv:[]
423 ~context:[] ~subst:[] pat))
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)
430 let select ~where status goal =
431 let name, _, _ as goalty = get_goal status goal in
432 let (wanted,_,where) = GrafiteDisambiguate.disambiguate_npattern where in
434 match where with None -> NCic.Implicit `Term | Some where -> where
436 let status, newgoalty = select_term status goalty (wanted,path) in
437 let status, instance =
438 mk_meta status ?name (`Term newgoalty) (`Decl newgoalty)
440 instantiate status goal instance
443 let select_tac ~where = distribute_tac (select ~where) ;;
445 let exact t status goal =
446 let goalty = get_goal status goal in
447 let status, t = disambiguate status t (Some goalty) (`Term goalty) in
448 instantiate status goal t
451 let exact_tac t = distribute_tac (exact t) ;;
454 let n,h,metasenv,subst,o = status.pstatus in
457 (function (_,(Some tag,_,_,_)) -> tag <> in_scope_tag && tag <> out_scope_tag
463 (function (_,(Some tag,_,_,_)) -> tag = in_scope_tag | _ -> assert false)
466 let metasenv = List.map (fun (i,(_,c,_,t)) -> i,(None,c,t)) in_m @ metasenv in
467 let in_m = List.map fst in_m in
468 let out_m = match out_m with [i] -> i | _ -> assert false in
469 { status with pstatus = n,h,metasenv,subst,o }, in_m, out_m
472 let change ~where ~with_what status goal =
473 let (name,_,_ as goalty) = get_goal status goal in
474 let (wanted,_,where) = GrafiteDisambiguate.disambiguate_npattern where in
476 match where with None -> NCic.Implicit `Term | Some where -> where
478 let status, newgoalty = select_term status goalty (wanted,path) in
479 let status, in_scope, out_scope = reopen status in
480 let status = List.fold_left (exact with_what) status in_scope in
482 let j,(n,cctx,bo,_) = out_scope in
483 let _ = typeof status (`Term goalty) (n,cctx,bo) in
485 let n,h,metasenv,subst,o = status.pstatus in
486 let subst = out_scope :: subst in
487 let status = { status with pstatus = n,h,metasenv,subst,o } in
489 let status, instance =
490 mk_meta status ?name (`Term newgoalty) (`Decl newgoalty)
492 instantiate status goal instance
495 let apply t status goal = exact t status goal;;
497 let apply_tac t = distribute_tac (apply t) ;;
498 let change_tac ~where ~with_what = distribute_tac (change ~where ~with_what) ;;
500 let elim_tac ~what ~where status =
503 distribute_tac (fun status goal ->
504 let goalty = get_goal status goal in
505 let status, (_,_,w as what) =
506 disambiguate status what None (`Term goalty) in
507 let _ty_what = typeof status (`Term what) what in
508 (* check inductive... find eliminator *)
509 let w = (*astify what *) CicNotationPt.Ident ("m",None) in
511 CicNotationPt.Implicit;CicNotationPt.Implicit;CicNotationPt.Implicit]
514 CicNotationPt.Appl(CicNotationPt.Ident("nat_ind",None)::holes @ [ w ])
516 exec (apply_tac ("",0,eliminator)) status goal) ]
522 ("",0,(CicNotationPt.Binder (`Lambda,
523 (CicNotationPt.Ident (name,None),None),CicNotationPt.Implicit)))