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 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
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
307 { status with pstatus = n,h,metasenv,subst,o }
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
314 match expty with None -> None, None
315 | Some e -> let n,_, e = relocate ctx e in Some n, Some e
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
324 { status with pstatus = (name,height,metasenv,subst,obj) },
325 (nt,ctx,t), (ne,ctx,ty)
328 let get_goal (status : lowtac_status) (g : int) =
329 let _,_,metasenv,_,_ = status.pstatus in
330 List.assoc g metasenv
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
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) }
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
348 let _,_,ty = relocate ctx ty in
349 let metasenv, _, instance, _ =
350 NCicMetaSubst.mk_meta ?name metasenv ctx (`WithType ty)
352 let status = { status with pstatus = n,h,metasenv,subst,o } in
353 status, (None,ctx,instance)
355 let _,_,ty = typeof status (`Ctx ctx) bo in
356 let metasenv, metano, instance, _ =
357 NCicMetaSubst.mk_meta ?name metasenv ctx (`WithType ty)
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)
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
369 | NCicUnification.UnificationFailure _
370 | NCicUnification.Uncertain _ -> false, status
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
376 let status, (_,_,t) =
377 mk_meta status ~name:in_scope_tag (`Ctx ctx) (`Def (None,ctx,t))
380 else NCicUntrusted.map_term_fold_a (fun e c -> e::c) ctx aux status t
384 let rec select status ctx pat cic =
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 ->
405 (fun (status,l) x y ->
406 let status, x = select status ctx x y 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
416 (fun (status,l) x y ->
417 let status, x = select status ctx x y in
421 status, NCic.Match (u,ot,t,List.rev pl)
422 | NCic.Implicit `Hole, t ->
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
430 fail (lazy ("malformed pattern: " ^ NCicPp.ppterm ~metasenv:[]
431 ~context:[] ~subst:[] pat))
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)
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
442 match where with None -> NCic.Implicit `Term | Some where -> where
444 let status, newgoalty = select_term status goalty (wanted,path) in
445 let status, instance =
446 mk_meta status ?name (`Term newgoalty) (`Decl newgoalty)
448 instantiate status goal instance
451 let select_tac ~where = distribute_tac (select ~where) ;;
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
459 let exact_tac t = distribute_tac (exact t) ;;
462 let n,h,metasenv,subst,o = status.pstatus in
465 (function (_,(Some tag,_,_,_)) -> tag <> in_scope_tag && tag <> out_scope_tag
471 (function (_,(Some tag,_,_,_)) -> tag = in_scope_tag | _ -> assert false)
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
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
484 match where with None -> NCic.Implicit `Term | Some where -> where
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
490 let j,(n,cctx,bo,_) = out_scope in
491 let _ = typeof status (`Term goalty) (n,cctx,bo) in
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
497 let status, instance =
498 mk_meta status ?name (`Term newgoalty) (`Decl newgoalty)
500 instantiate status goal instance
503 let apply t status goal = exact t status goal;;
505 let apply_tac t = distribute_tac (apply t) ;;
506 let change_tac ~where ~with_what = distribute_tac (change ~where ~with_what) ;;
508 let elim_tac ~what ~where status =
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
519 CicNotationPt.Implicit;CicNotationPt.Implicit;CicNotationPt.Implicit]
522 CicNotationPt.Appl(CicNotationPt.Ident("nat_ind",None)::holes @ [ w ])
524 exec (apply_tac ("",0,eliminator)) status goal) ]
530 ("",0,(CicNotationPt.Binder (`Lambda,
531 (CicNotationPt.Ident (name,None),None),CicNotationPt.Implicit)))
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
538 match whd status (`Ctx ctx) ty with
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
546 NCic.Match (ref,NCic.Implicit `Term,NCic.Rel 1,HExtlib.mk_list (NCic.Implicit `Term) consno)
548 let status,t,ty = refine status (`Ctx ctx) ("",ctx,t) None in
549 instantiate status goal t
552 let case_tac = distribute_tac case;;
555 block_tac [ intro_tac name; case_tac ]