1(****************************************************************************)
2(* Copyright (C) 2007-2009 Gacek                                            *)
3(* Copyright (C) 2013-2018 Inria (Institut National de Recherche            *)
4(*                         en Informatique et en Automatique)               *)
5(*                                                                          *)
6(* This file is part of Abella.                                             *)
7(*                                                                          *)
8(* Abella is free software: you can redistribute it and/or modify           *)
9(* it under the terms of the GNU General Public License as published by     *)
10(* the Free Software Foundation, either version 3 of the License, or        *)
11(* (at your option) any later version.                                      *)
12(*                                                                          *)
13(* Abella is distributed in the hope that it will be useful,                *)
14(* but WITHOUT ANY WARRANTY; without even the implied warranty of           *)
15(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *)
16(* GNU General Public License for more details.                             *)
17(*                                                                          *)
18(* You should have received a copy of the GNU General Public License        *)
19(* along with Abella.  If not, see <http://www.gnu.org/licenses/>.          *)
20(****************************************************************************)
21
22open Term
23open Typing
24open Metaterm
25open Format
26open Tactics
27open Checks
28open Abella_types
29open Extensions
30open Unifyty
31
32module H = Hashtbl
33
34let lemmas : (string, string list * metaterm) H.t = State.table ()
35
36type subgoal = unit -> unit
37let subgoals : subgoal list ref = State.rref []
38
39type hyp = {
40  id : id ;
41  term : metaterm ;
42  abbrev : string option ;
43}
44
45type sequent = {
46  mutable vars : (id * term) list ;
47  mutable hyps : hyp list ;
48  mutable goal : metaterm ;
49  mutable count : int ;
50  mutable name : string ;
51  mutable next_subgoal_id : int ;
52}
53
54(* The vars = sq.vars is superfluous, but forces the copy *)
55let cp_sequent sq = {sq with vars = sq.vars}
56let assign_sequent sq1 sq2 =
57  sq1.vars <- sq2.vars ;
58  sq1.hyps <- sq2.hyps ;
59  sq1.goal <- sq2.goal ;
60  sq1.count <- sq2.count ;
61  sq1.name <- sq2.name ;
62  sq1.next_subgoal_id <- sq2.next_subgoal_id
63
64let sequent =
65  State.make ~copy:cp_sequent ~assign:assign_sequent {
66    vars = [] ;
67    hyps = [] ;
68    goal = termobj (const "placeholder" propty) ;
69    count = 0 ;
70    name = "" ;
71    next_subgoal_id = 1 ;
72  }
73
74let add_global_types tys knd =
75  sign := add_types !sign tys knd
76
77let locally_add_global_consts cs =
78  let local_sr = List.fold_left Subordination.update !sr (List.map snd cs)
79  and local_sign = add_consts !sign cs
80  in (local_sr, local_sign)
81
82let commit_global_consts local_sr local_sign =
83  sr := local_sr ;
84  sign := local_sign
85
86let add_global_consts cs =
87  sr := List.fold_left Subordination.update !sr (List.map snd cs) ;
88  sign := add_consts !sign cs
89
90let close_types sign clauses atys =
91  List.iter (kind_check sign) (List.map tybase atys);
92  let tys = List.map tybase atys in
93  begin match List.intersect [oty; olistty; propty] tys with
94  | [] -> ()
95  | xs -> failwithf "Cannot close %s"
96     (String.concat ", " (List.map ty_to_string xs))
97  end ;
98  sr := Subordination.close !sr atys;
99  List.iter (Typing.term_ensure_subordination !sr) (List.map snd clauses)
100
101let add_subgoals ?(mainline) new_subgoals =
102  let extend_name i =
103    if sequent.name = "" then
104      sequent.name <- string_of_int i
105    else
106      sequent.name <- sequent.name ^ "." ^ (string_of_int i)
107  in
108  let rec annotate i gs =
109    match gs with
110    | [] -> []
111    | g::rest ->
112        (fun () -> g (); extend_name i; sequent.next_subgoal_id <- 1) ::
113        annotate (i+1) rest
114  in
115  let n = List.length new_subgoals in
116  let annotated_subgoals =
117    match mainline with
118    | None ->
119        if n > 1 then
120          annotate sequent.next_subgoal_id new_subgoals
121        else
122          new_subgoals
123    | Some mainline ->
124        let new_mainline () =
125          mainline () ;
126          sequent.next_subgoal_id <- sequent.next_subgoal_id + n
127        in
128        annotate sequent.next_subgoal_id new_subgoals @ [new_mainline]
129  in
130  subgoals := annotated_subgoals @ !subgoals
131
132let copy_sequent () = cp_sequent sequent
133let set_sequent other = assign_sequent sequent other
134
135let fresh_hyp_name base =
136  if base = "" then begin
137    sequent.count <- sequent.count + 1 ;
138    "H" ^ (string_of_int sequent.count)
139  end else
140    fresh_name base (List.map (fun h -> (h.id, ())) sequent.hyps)
141
142(* let normalize mt = *)
143(*   let before = metaterm_to_string mt in *)
144(*   Printf.fprintf stderr "normalizing\n%s\n%!" before ; *)
145(*   let mt = normalize mt in *)
146(*   let after = metaterm_to_string mt in *)
147(*   Printf.fprintf stderr "normalized form\n%s\n%!" after ; *)
148(*   mt *)
149
150let normalize_sequent () =
151  (* Printf.fprintf stderr "Normalizing Sequent %s\n%!" sequent.name ; *)
152  sequent.goal <- normalize sequent.goal ;
153  sequent.hyps <-
154    sequent.hyps |> List.map (fun h -> { h with term = normalize h.term })
155
156(* Clauses *)
157
158let clauses : clause list ref = State.rref []
159
160let add_clauses new_clauses =
161  clauses := !clauses @ new_clauses
162
163let parse_defs ?(sign = !sign) str =
164  Lexing.from_string str |>
165  Parser.defs Lexer.token |>
166  type_udefs ~sr:!sr ~sign |>
167  List.map (fun (head, body) -> {head ; body})
168
169let defs_table : defs_table = State.table ()
170
171let built_ins_done = ref false
172
173let add_defs typarams preds flavor clauses =
174  List.iter begin fun (id, _) ->
175    if H.mem defs_table id then
176      failwithf "Predicate %s has already been defined" id ;
177  end preds ;
178  (* List.iter begin fun (head, body) -> *)
179  (*   Format.eprintf "%a := %a@." format_metaterm head format_metaterm body *)
180  (* end defs ; *)
181  let mutual = List.fold_left begin fun mutual (id, ty) ->
182      Itab.add id ty mutual
183    end Itab.empty preds in
184  let def = {flavor ; typarams ; mutual ; clauses} in
185  Checks.check_def ~def ;
186  List.iter (fun (id, _) -> H.add defs_table id def) preds
187
188let lookup_poly_const k =
189  try let Poly (typarams, ty) = List.assoc k (snd !sign) in (typarams, ty) with
190  | Not_found -> failwithf "Unknown constant: %S" k
191
192
193let register_definition = function
194  | Define (flav, idtys, udefs) ->
195      let typarams = List.unique (idtys |> List.map snd |> get_typarams) in
196      check_typarams typarams (List.map snd idtys);
197      let ids = List.map fst idtys in
198      check_noredef ids;
199      let (basics, consts) = !sign in
200      (* Note that in order to type check the definitions, the types
201         of predicates being defined are fixed to their most generate
202         form by fixing the type variables in them to generic ones *)
203      let consts = List.map (fun (id, ty) -> (id, Poly ([], ty))) idtys @ consts in
204      let clauses = type_udefs ~sr:!sr ~sign:(basics, consts) udefs |>
205                    List.map (fun (head, body) -> {head ; body}) in
206      ensure_no_schm_clauses typarams clauses;
207      let (basics, consts) = !sign in
208      let consts = List.map (fun (id, ty) -> (id, Poly (typarams, ty))) idtys @ consts in
209      sign := (basics, consts) ;
210      add_defs typarams idtys flav clauses ;
211      CDefine (flav, typarams, idtys, clauses)
212  | _ -> bugf "Not a definition!"
213
214let parse_definition str =
215  Lexing.from_string str |>
216  Parser.top_command Lexer.token |>
217  register_definition
218
219let k_member = "member"
220let member_def_compiled =
221  "Define " ^ k_member ^ " : A -> list A -> prop by\
222  \  " ^ k_member ^ " A (A :: L) ; \
223  \  " ^ k_member ^ " A (B :: L) := " ^ k_member ^ " A L." |>
224  parse_definition
225
226let () = built_ins_done := true
227
228let term_spine t =
229  match term_head t with
230  | Some (_, spine) -> spine
231  | None -> assert false
232
233let clause_head_name cl =
234  match cl.head with
235  | Binding (Nabla, _, Pred (p, _)) | Pred (p, _) ->
236      term_head_name p
237  | _ -> bugf "Clause head name for invalid clause: %s" (metaterm_to_string cl.head)
238
239(* let rec app_ty tymap = function
240 *   | Ty (args, res) ->
241 *       let args = List.map (app_ty tymap) args in
242 *       let res = try Itab.find res tymap with Not_found -> tybase res in
243 *       tyarrow args res *)
244
245(* let instantiate_clauses_aux =
246 *   let fn (pn, ty_acts) def =
247 *     let Ty (ty_exps, _) = Itab.find pn def.mutual in
248 *     let ty_fresh = List.fold_left begin fun fresh_sub tyvar ->
249 *         let tv = Term.fresh_tyvar () in
250 *         Itab.add tyvar tv fresh_sub
251 *       end Itab.empty def.typarams in
252 *     let ty_exps = List.map (app_ty ty_fresh) ty_exps in
253 *     let eqns = List.map2 begin fun ty_exp ty_act ->
254 *         (ty_exp, ty_act, (ghost, CArg))
255 *       end ty_exps ty_acts in
256 *     let tysol = unify_constraints eqns in
257 *     let tymap = List.fold_left begin fun tymap tyv ->
258 *         try begin
259 *           let Ty (_, tyf) = Itab.find tyv ty_fresh in
260 *           Itab.add tyv (List.assoc tyf tysol) tymap
261 *         end with Not_found -> tymap
262 *       end Itab.empty def.typarams in
263 *     (\* Itab.iter begin fun v ty -> *\)
264 *     (\*   Format.eprintf "instantiating: %s <- %s@." v (ty_to_string ty) *\)
265 *     (\* end tymap ; *\)
266 *     List.map begin fun cl ->
267 *       if clause_head_name cl = pn then
268 *         {head = map_on_tys (app_ty tymap) cl.head ;
269 *          body = map_on_tys (app_ty tymap) cl.body}
270 *       else cl
271 *     end def.clauses
272 *   in
273 *   memoize fn *)
274
275(* let instantiate_clauses pn def args =
276 *   let ty_acts = List.map (tc []) args in
277 *   instantiate_clauses_aux (pn, ty_acts) def *)
278
279let instantiate_pred_types tysub (tbl: ty Itab.t) : ty Itab.t =
280  let res = ref Itab.empty in
281  Itab.iter begin fun id ty ->
282    let ty' = apply_sub_ty tysub ty in
283    res := Itab.add id ty' !res
284    end tbl;
285  !res
286
287let instantiate_clause tysub cl =
288  let head = map_on_tys (apply_sub_ty tysub) cl.head in
289  let body = map_on_tys (apply_sub_ty tysub) cl.body in
290  {head = head; body = body}
291
292let instantiate_clauses p def =
293  let pn = term_head_name p in
294  let pty = term_head_ty p in
295  let clauses = def.clauses in
296  let tysub = List.map (fun id -> (id, Term.fresh_tyvar ())) def.typarams in
297  let mutual = instantiate_pred_types tysub def.mutual in
298  let dpty = Itab.find pn mutual in
299  unify_constraints [(pty, dpty, def_cinfo)];
300  assert (ty_tyvars dpty = []);
301  let clauses = List.map (instantiate_clause tysub) clauses in
302  (mutual, clauses)
303
304let def_unfold term =
305  match term with
306  | Pred(p, _) ->
307     let pn = term_head_name p in
308      if H.mem defs_table pn then begin
309        let def = H.find defs_table pn in
310        let (mutual, clauses) = instantiate_clauses p def in
311        List.iter begin fun cl ->
312          metaterm_ensure_subordination !sr cl.head ;
313          metaterm_ensure_subordination !sr cl.body
314          end clauses;
315        (mutual,clauses)
316      end else
317        failwith "Cannot perform case-analysis on undefined atom"
318  | _ -> (Itab.empty, [])
319
320
321(* Pretty print *)
322
323let sequent_var_to_string (x, _xt) =
324  x (* ^ "(=" ^ term_to_string xt ^ ")" *)
325
326let show_instantiations = State.rref false
327
328let separate_strings xs =
329  let __max_len = 30 in
330  let result = ref [] in
331  let emit xs = result := String.concat "," (List.rev xs) :: !result in
332  let rec spin nxs pos = function
333    | [] -> emit nxs
334    | x :: xs ->
335      if pos + String.length x + 1 < __max_len then
336        spin (x :: nxs) (pos + String.length x + 1) xs
337      else begin
338        emit nxs ;
339        spin [x] (String.length x) xs
340      end
341  in
342  spin [] 0 xs ;
343  List.rev !result
344
345let format_typed_vars ff (ty, xs) =
346  let open Format in
347  List.iter begin fun xs ->
348    pp_print_string ff "  " ;
349    pp_print_string ff xs ;
350    pp_print_string ff " : " ;
351    pp_open_box ff 0 ; Term.format_ty ff ty ; pp_close_box ff () ;
352    pp_print_newline ff () ;
353  end (separate_strings xs)
354
355let format_vars ff =
356  let open Format in
357  let (eigen_vars, inst_vars) =
358    List.partition is_uninstantiated sequent.vars in
359  if eigen_vars <> [] then begin
360    pp_print_string ff "Variables: " ;
361    if !show_types then begin
362      pp_print_newline ff () ;
363      eigen_vars
364      |> List.map
365        (fun (x, xtm) -> (Term.tc [] xtm, x))
366      |> List.sort
367        (fun (ty1, _) (ty2, _) -> compare ty1 ty2)
368      |> List.collate_assoc
369      |> List.iter (format_typed_vars ff) ;
370    end else begin
371      pp_open_hovbox ff 0 ; begin
372        pp_print_string ff (sequent_var_to_string (List.hd eigen_vars)) ;
373        List.iter begin
374          fun v ->
375            pp_print_space ff () ;
376            pp_print_string ff (sequent_var_to_string v)
377        end (List.tl eigen_vars) ;
378      end ; pp_close_box ff () ;
379      pp_print_newline ff ()
380    end
381  end ;
382  if inst_vars <> [] && !show_instantiations then
383    List.iter begin fun (v, vtm) ->
384      pp_print_string ff v ;
385      pp_print_string ff " <-- " ;
386      Term.format_term ff vtm ;
387      pp_print_newline ff () ;
388    end inst_vars
389
390let format_hyp fmt hyp =
391  fprintf fmt "%s : " hyp.id ;
392  begin match hyp.abbrev with
393  | None -> format_metaterm fmt hyp.term
394  | Some s -> fprintf fmt "%s" s
395  end;
396  pp_force_newline fmt ()
397
398let format_hyps fmt =
399  let (am, hyps) = List.fold_left begin fun (am, hs) h ->
400      match h.abbrev with
401      | None -> (am, h :: hs)
402      | Some ab ->
403          let am = match Itab.find ab am with
404            | hs -> Itab.add ab (h.id :: hs) am
405            | exception Not_found -> Itab.add ab [h.id] am
406          in
407          (am, hs)
408    end (Itab.empty, []) sequent.hyps
409  in
410  Itab.iter begin fun ab hs ->
411    fprintf fmt "%s : %s@\n" (String.concat "," (List.rev hs)) ab
412  end am ;
413  List.iter (format_hyp fmt) (List.rev hyps)
414
415let format_count_subgoals fmt n =
416  match n with
417  | 0 -> ()
418  | 1 -> fprintf fmt "1 other subgoal.@\n@\n"
419  | n -> fprintf fmt "%d other subgoals.@\n@\n" n
420
421type subgoal_max = {
422  smax_default : int ;
423  smax_map : int IntMap.t ;
424}
425let subgoal_max_init = { smax_default = max_int ;
426                         smax_map = IntMap.empty }
427let subgoal_max : subgoal_max ref = State.rref subgoal_max_init
428let reset_subgoal_max () =
429  subgoal_max := subgoal_max_init
430let set_subgoal_max_default smax_default =
431  let smax = !subgoal_max in
432  subgoal_max := { smax with smax_default }
433let set_subgoal_max ndps =
434  let smax = !subgoal_max in
435  let smax_map = List.fold_left begin
436      fun smax_map (dp, n) ->
437        let n = Option.default max_int n in
438        (* Printf.printf "Max subgoals at depth %d: %d\n%!" dp n ; *)
439        IntMap.add dp n smax_map
440    end smax.smax_map ndps in
441  subgoal_max := { smax with smax_map }
442
443let format_other_subgoals fmt =
444  let pristine = State.snapshot () in
445  let smax = !subgoal_max in
446  let smax_map = ref smax.smax_map in
447  let count = ref 0 in
448  List.iter begin fun set_state ->
449    set_state () ;
450    let goal_depth = String.count sequent.name '.' in
451    let max_goals =  try IntMap.find goal_depth !smax_map with Not_found -> smax.smax_default in
452    (* Printf.printf "Showing %d goals at depth %d\n%!" max_goals goal_depth ; *)
453    if max_goals > 0 then begin
454      smax_map := IntMap.add goal_depth (max_goals - 1) !smax_map ;
455      fprintf fmt "@[<1>Subgoal %s%sis:@\n%a@]@\n@\n"
456        sequent.name
457        (if sequent.name = "" then "" else " ")
458        format_metaterm (normalize sequent.goal)
459    end else incr count
460  end !subgoals ;
461  format_count_subgoals fmt !count ;
462  State.reload pristine
463
464let format_sequent fmt =
465  pp_open_vbox fmt 0 ; begin
466    format_vars fmt ;
467    format_hyps fmt ;
468    fprintf fmt "============================@\n " ;
469    format_metaterm fmt sequent.goal
470  end ; pp_close_box fmt ()
471
472let format_display fmt =
473  pp_open_box fmt 0 ;
474  if sequent.name = "" then
475    fprintf fmt "@\n"
476  else
477    fprintf fmt "Subgoal %s:@\n@\n" sequent.name;
478  format_sequent fmt ;
479  fprintf fmt "@\n@\n" ;
480  format_other_subgoals fmt ;
481  pp_close_box fmt () ;
482  pp_print_flush fmt ()
483
484let display out =
485  format_display (formatter_of_out_channel out)
486
487let get_display () =
488  let b = Buffer.create 100 in
489  format_display (formatter_of_buffer b) ;
490  Buffer.contents b
491
492
493(* Proof state manipulation utilities *)
494
495let reset_prover original_state original_sequent =
496  (* let original_state = get_bind_state () in
497   * let original_sequent = copy_sequent () in *)
498  fun () ->
499    set_bind_state original_state ;
500    set_sequent original_sequent ;
501    subgoals := []
502
503let full_reset_prover original_state original_sequent
504  original_clauses original_defs_table =
505  (* let original_clauses = !clauses in
506   * let original_defs_table = H.copy defs_table in *)
507  fun () ->
508    reset_prover original_state original_sequent () ;
509    clauses := original_clauses ;
510    H.assign defs_table original_defs_table
511
512let add_hyp ?name term =
513  let name = fresh_hyp_name begin
514      match name with
515      | None -> ""
516      | Some name -> name
517    end in
518  sequent.hyps <- List.append sequent.hyps
519      [{ id = name ; term = term ; abbrev = None }]
520
521let remove_hyp cm name =
522  let removed_h = ref None in
523  let hyps = List.filter begin fun h ->
524      h.id <> name || begin
525        removed_h := Some h ;
526        false
527      end
528    end sequent.hyps in
529  sequent.hyps <- hyps ;
530  match cm, !removed_h with
531  | Clear_extro, Some h ->
532      sequent.goal <- Arrow (h.term, sequent.goal)
533  | Clear_extro, None ->
534      failwithf "Cannot extrude unknown hypothesis %s" name
535  | _ -> ()
536
537let replace_hyp name t =
538  let rec aux hyplist =
539    match hyplist with
540    | [] -> []
541    | hyp::rest when hyp.id = name -> {hyp with term = t} :: rest
542    | hyp::rest -> hyp :: (aux rest)
543  in
544  sequent.hyps <- aux sequent.hyps
545
546let add_var v =
547  sequent.vars <- List.append sequent.vars [v]
548
549let add_if_new_var (name, v) =
550  if not (List.mem_assoc name sequent.vars) then
551    add_var (name, v)
552
553let add_lemma name tys lemma =
554  if H.mem lemmas name then
555    Format.eprintf "Warning: overriding existing lemma named %S@." name ;
556  H.replace lemmas name (tys, lemma)
557
558let get_hyp name =
559  let hyp = List.find (fun h -> h.id = name) sequent.hyps in
560  hyp.term
561
562let is_hyp name =
563  List.exists (fun h -> h.id = name) sequent.hyps
564
565let get_generic_lemma name =
566  try H.find lemmas name with
567  | Not_found ->
568      failwithf "Could not find theorem named %S" name
569
570let get_lemma ?(tys:ty list = []) name =
571  let (argtys, bod) = H.find lemmas name in
572  let tys =
573    if (List.length argtys > 0 && List.length tys = 0) then
574      List.map (fun _t -> fresh_tyvar ()) argtys
575    else if List.length tys <> List.length argtys then
576      failwithf "Need to provide mappings for %d types" (List.length argtys)
577    else
578      tys in
579  let tysub = List.map2 (fun id ty -> (id,ty)) argtys tys in
580  map_on_tys (apply_sub_ty tysub) bod
581
582let get_hyp_or_lemma ?tys name =
583  try get_hyp name with
584  | Not_found -> get_lemma ?tys name
585
586let stmt_name = function
587  | Keep (h, _) | Remove (h, _) -> h
588
589let get_stmt_clearly h =
590  try begin
591    match h with
592    | Keep (h, tys) ->
593        get_hyp_or_lemma ~tys h
594    | Remove (h, []) when is_hyp h ->
595        let stmt = get_hyp h in
596        remove_hyp Clear_delete h ; stmt
597    | Remove (h, tys) ->
598        get_lemma ~tys h
599  end
600  with Not_found ->
601    failwithf "Could not find hypothesis or lemma %s" (stmt_name h)
602
603let get_arg_clearly = function
604  | Keep ("_", _) -> None
605  | arg -> Some (get_stmt_clearly arg)
606
607exception End_proof of [`completed | `aborted]
608
609let next_subgoal () =
610  match !subgoals with
611  | [] -> raise (End_proof `completed)
612  | set_subgoal::rest ->
613      set_subgoal () ;
614      subgoals := rest ;
615      normalize_sequent ()
616
617(* Show *)
618
619let print_theorem name (tys, thm) =
620  let ff = Format.formatter_of_out_channel !Checks.out in
621  Format.fprintf ff "@[<hv2>Theorem %s%s :@ %a@].@."
622    name (gen_to_string tys) format_metaterm thm
623
624let show name =
625  print_theorem name (get_generic_lemma name)
626
627(* Object level instantiation *)
628
629let inst ?name h ws =
630  let ht = get_stmt_clearly h in
631  match ht with
632  | Obj _ ->
633      let rec aux ws ht = match ws with
634        | [] -> add_hyp ?name ht
635        | (n, t) :: ws ->
636            let ntids = metaterm_nominal_tids ht in
637            let nty = try List.assoc n ntids with
638              | Not_found -> failwithf "Nominal constant %s not in support" n in
639            let ctx = sequent.vars @
640                      (List.map (fun (id, ty) -> (id, nominal_var id ty)) ntids)
641            in
642            let t = type_uterm ~expected_ty:nty ~sr:!sr ~sign:!sign ~ctx t in
643            aux ws (object_inst ht n t)
644      in
645      aux ws ht
646  | _ ->
647      failwith "Cannot instantiate this hypothesis\n\
648              \ Instantiation can only be used on hypotheses of the form {...}"
649
650
651(* Object level cut *)
652
653let cut ?name h arg =
654  let h = get_stmt_clearly h in
655  let arg = get_stmt_clearly arg in
656  match h, arg with
657  | Obj(obj_h, _), Obj(obj_arg, _) ->
658      add_hyp ?name (object_cut obj_h obj_arg)
659  | _ -> failwith "Cut can only be used on hypotheses of the form {...}"
660
661(* Search *)
662
663let retype t = type_uterm ~sr:!sr ~sign:!sign ~ctx:sequent.vars t
664
665let has_inductive_hyps hyp =
666  let rec aux term =
667    match term with
668    | Binding(Forall, _, body) -> aux body
669    | Binding(Nabla, _, body) -> aux body
670    | Arrow(Obj(_, Smaller _i), _) -> true
671    | Arrow(Pred(_, Smaller _i), _) -> true
672    | Arrow(_left, right) -> aux right
673    | _ -> false
674  in
675  aux hyp.term
676
677let remove_inductive_hypotheses hyps =
678  List.remove_all has_inductive_hyps hyps
679
680let has_coinductive_result hyp =
681  let rec aux term nested =
682    match term with
683    | Binding(Forall, _, body) -> aux body true
684    | Binding(Nabla, _, body) -> aux body true
685    | Arrow(_left, right) -> aux right true
686    | Pred(_, CoSmaller _) -> nested
687    | Pred(_, CoEqual _) -> nested
688    | _ -> false
689  in
690  aux hyp.term false
691
692let remove_coinductive_hypotheses hyps =
693  List.remove_all has_coinductive_result hyps
694
695let search_depth = State.rref 5
696
697let search_goal_witness ?depth goal witness =
698  let hyps = sequent.hyps
699             |> remove_inductive_hypotheses
700             |> remove_coinductive_hypotheses
701             |> List.map (fun h -> (h.id, h.term))
702  in
703  let depth = Option.default !search_depth depth in
704  Tactics.search
705    ~depth
706    ~hyps
707    ~clauses:!clauses
708    ~def_unfold
709    ~sr:!sr
710    ~retype
711    ~witness
712    goal
713
714let search_goal ?depth goal =
715  try Option.is_some (search_goal_witness ?depth goal WMagic)
716  with Failure _ -> false
717
718let search ?depth ~witness ~handle_witness () =
719  let search_result = search_goal_witness ?depth sequent.goal witness in
720  match search_result with
721  | None -> failwith "Search failed"
722  | Some w -> handle_witness w ; next_subgoal ()
723
724let async () =
725  failwith "async not implemented"
726
727(* Search cut *)
728
729let search_cut ?name h =
730  match get_stmt_clearly h with
731  | Obj(obj, _) ->
732      add_hyp ?name (Obj(search_cut ~search_goal obj, Irrelevant))
733  | _ ->
734      failwith "Cut can only be used on hypotheses of the form {... |- ...}"
735
736(* Apply *)
737
738let goal_to_subgoal g =
739  let saved_sequent = copy_sequent () in
740  let bind_state = Term.get_bind_state () in
741  fun () ->
742    set_sequent saved_sequent ;
743    Term.set_bind_state bind_state ;
744    sequent.goal <- g
745
746let ensure_no_logic_variable terms =
747  let logic_vars = List.flatten_map (metaterm_vars_alist Logic) terms in
748  if logic_vars <> [] then
749    failwith "Found logic variable at toplevel"
750
751let ensure_no_restrictions term =
752  let rec aux t nested =
753    match t with
754    | Binding(Forall, _, body) -> aux body true
755    | Binding(Nabla, _, body) -> aux body true
756    | Arrow(left, right) -> aux left true; aux right true
757    | Obj(_, Smaller _i) | Obj(_, Equal _i)
758    | Pred(_, Smaller _i) | Pred(_, Equal _i) ->
759        if nested then
760          failwithf "Inductive restrictions must not occur in strict subterms:\n%s"
761            (metaterm_to_string term)
762    | Pred(_, CoSmaller _i) | Pred(_, CoEqual _i) ->
763        failwithf "Co-Inductive restrictions must not be present:\n%s"
764          (metaterm_to_string term)
765    | _ -> ()
766  in
767  aux term false
768
769let toplevel_bindings stmt =
770  let rec aux = function
771    | Binding(Forall, tids, t) -> tids @ (aux t)
772    | Binding(Nabla, tids, t) -> tids @ (aux t)
773    | _ -> []
774  in
775  aux stmt
776
777let type_apply_withs stmt ws =
778  let bindings = toplevel_bindings stmt in
779  List.map
780    (fun (id, t) ->
781       try
782         let ty = List.assoc id bindings in
783         (id, type_uterm ~expected_ty:ty ~sr:!sr ~sign:!sign ~ctx:sequent.vars t)
784       with
785       | Not_found -> failwithf "Unknown variable %s" id)
786    ws
787
788let partition_obligations ?depth obligations =
789  Either.partition_eithers
790    (List.map
791       (fun g -> match search_goal_witness ?depth g WMagic with
792          | None -> Either.Left g
793          | Some w -> Either.Right (g, w))
794       obligations)
795
796let apply ?depth ?name ?(term_witness=ignore) h args ws =
797  let stmt = get_stmt_clearly h in
798  let args = List.map get_arg_clearly args in
799  let () = List.iter (Option.map_default ensure_no_restrictions ()) args in
800  let ws = type_apply_withs stmt ws in
801  let result, obligations = Tactics.apply_with stmt args ws in
802  let remaining_obligations, term_witnesses =
803    partition_obligations ?depth obligations
804  in
805  let () = ensure_no_logic_variable (result :: remaining_obligations) in
806  let () = List.iter term_witness term_witnesses in
807  let obligation_subgoals = List.map goal_to_subgoal remaining_obligations in
808  let resulting_case = recursive_metaterm_case ~used:sequent.vars ~sr:!sr result in
809  begin match resulting_case with
810  | None -> add_subgoals obligation_subgoals
811  | Some case ->
812      let resulting_subgoal =
813        let restore = goal_to_subgoal sequent.goal in
814        fun () ->
815          restore () ;
816          List.iter add_if_new_var case.stateless_new_vars ;
817          List.iter (add_hyp ?name) case.stateless_new_hyps
818      in
819      add_subgoals ~mainline:resulting_subgoal obligation_subgoals
820  end ;
821  next_subgoal ()
822
823(* Bachchain *)
824
825let type_backchain_withs stmt ws =
826  let bindings = toplevel_bindings stmt in
827  let nctx = List.map term_to_pair (metaterm_support sequent.goal) in
828  List.map
829    (fun (id, t) ->
830       try
831         let ty = List.assoc id bindings in
832         (id, type_uterm ~expected_ty:ty ~sr:!sr ~sign:!sign
833            ~ctx:(nctx @ sequent.vars) t)
834       with
835       | Not_found -> failwithf "Unknown variable %s" id)
836    ws
837
838let backchain ?depth ?(term_witness=ignore) h ws =
839  let stmt = get_stmt_clearly h in
840  let ws = type_backchain_withs stmt ws in
841  let obligations = Tactics.backchain_with stmt ws sequent.goal in
842  let remaining_obligations, term_witnesses =
843    partition_obligations ?depth obligations
844  in
845  let () = ensure_no_logic_variable remaining_obligations in
846  let () = List.iter term_witness term_witnesses in
847  let obligation_subgoals = List.map goal_to_subgoal remaining_obligations in
848  add_subgoals obligation_subgoals ;
849  next_subgoal ()
850
851(* Case analysis *)
852
853(* Lifting during case analysis may cause some variables to be bound to
854   themselves. We need to update sequent.vars to reflect this. *)
855let update_self_bound_vars () =
856  sequent.vars <-
857    sequent.vars |> List.map
858      (fun (id, term) ->
859         match term_head term with
860         | Some (v, _) when term_to_name v = id ->
861             (id, v)
862         | _ -> (id, term))
863
864let case_to_subgoal ?name case =
865  let saved_sequent = copy_sequent () in
866  fun () ->
867    set_sequent saved_sequent ;
868    List.iter add_if_new_var case.new_vars ;
869    List.iter (add_hyp ?name) case.new_hyps ;
870    Term.set_bind_state case.bind_state ;
871    update_self_bound_vars ()
872
873let case ?name str =
874  let global_support =
875    (List.flatten_map metaterm_support
876       (List.map (fun h -> h.term) sequent.hyps)) @
877    (metaterm_support sequent.goal)
878  in
879  let term = get_stmt_clearly str in
880  let (mutual, defs) = def_unfold term in
881  let cases =
882    Tactics.case ~used:sequent.vars ~sr:!sr ~clauses:!clauses
883      ~mutual ~defs ~global_support term
884  in
885  add_subgoals (List.map (case_to_subgoal ?name) cases) ;
886  next_subgoal ()
887
888
889(* Induction *)
890
891let next_restriction () =
892  1 + (sequent.hyps |> List.map (fun h -> h.term) |>
893       List.map get_max_restriction |> List.max)
894
895let rec nth_product n term =
896  match term with
897  | Binding(Forall, _, body) -> nth_product n body
898  | Binding(Nabla, _, body) -> nth_product n body
899  | Arrow(left, right) ->
900      if n = 1 then
901        left
902      else
903        nth_product (n-1) right
904  | _ -> failwith "Can only induct on predicates and judgments"
905
906let ensure_is_inductive term =
907  match term with
908  | Obj _ -> ()
909  | Pred(p, _) ->
910      let pname = term_head_name p in
911      begin try
912        match (H.find defs_table pname).flavor with
913        | Inductive -> ()
914        | CoInductive ->
915            failwithf "Cannot induct on %s since it has\
916                     \ been coinductively defined"
917              pname
918      with Not_found ->
919        failwithf "Cannot induct on %s since it has not been defined" pname
920      end
921  | _ -> failwith "Can only induct on predicates and judgments"
922
923let add_ih h =
924  add_hyp ~name:(fresh_hyp_name "IH") h
925
926let induction ?name ind_args =
927  if has_coinductive_restriction sequent.goal then
928    failwith "Induction within coinduction is not allowed" ;
929  let stmts = and_to_list sequent.goal in
930  if List.length ind_args != List.length stmts then
931    failwithf "Expecting %d induction arguments but got %d"
932      (List.length stmts) (List.length ind_args) ;
933  List.iter
934    (fun (arg, goal) -> ensure_is_inductive (nth_product arg goal))
935    (List.combine ind_args stmts) ;
936  let res_num = next_restriction () in
937  let (ihs, new_goal) = Tactics.induction ind_args res_num sequent.goal in
938  let name = match name with
939    | None -> fresh_hyp_name "IH"
940    | Some name -> name
941  in
942  List.iter (add_hyp ~name) ihs ;
943  sequent.goal <- new_goal
944
945
946(* CoInduction *)
947
948let rec conclusion term =
949  match term with
950  | Binding(Forall, _, body) -> conclusion body
951  | Binding(Nabla, _, body) -> conclusion body
952  | Arrow(_left, right) -> conclusion right
953  | Pred(p, _) -> p
954  | _ -> failwith "Cannot coinduct on a goal of this form"
955
956let ensure_is_coinductive p =
957  let pname = term_head_name p in
958  try
959    match (H.find defs_table pname).flavor with
960    | CoInductive -> ()
961    | Inductive ->
962        failwithf "Cannot coinduct on %s since it has\
963                 \ been inductively defined"
964          pname
965  with Not_found ->
966    failwithf "Cannot coinduct on %s since it has not been defined" pname
967
968let coinduction ?name () =
969  ensure_is_coinductive (conclusion sequent.goal) ;
970  if has_inductive_restriction sequent.goal then
971    failwith "Coinduction within induction is not allowed" ;
972  let res_num = next_restriction () in
973  let (ch, new_goal) = Tactics.coinduction res_num sequent.goal in
974  let name = match name with
975    | None -> fresh_hyp_name "CH"
976    | Some name -> name
977  in
978  add_hyp ~name ch ;
979  sequent.goal <- new_goal
980
981
982(* Assert *)
983
984let delay_mainline ?name ?depth new_hyp detour_goal =
985  if depth <> Some 0 && unwind_state (search_goal ?depth) detour_goal then
986    add_hyp ?name new_hyp
987  else
988  let mainline =
989    case_to_subgoal ?name
990      { bind_state = get_bind_state () ;
991        new_vars = [] ;
992        new_hyps = [new_hyp] }
993  in
994  let detour = goal_to_subgoal detour_goal in
995  (* Using add_subgoals to take care of annotations *)
996  add_subgoals ~mainline [detour] ;
997  next_subgoal ()
998
999let assert_hyp ?name ?depth term =
1000  let term = type_umetaterm ~sr:!sr ~sign:!sign ~ctx:sequent.vars term in
1001  delay_mainline ?name ?depth term term
1002
1003(* Object logic monotone *)
1004
1005let monotone ?name h t =
1006  let ht = get_stmt_clearly h in
1007  match ht with
1008  | Obj (obj, r) ->
1009      let ntids = metaterm_nominal_tids ht in
1010      let ctx = sequent.vars @
1011                (List.map (fun (id, ty) -> (id, nominal_var id ty)) ntids)
1012      in
1013      let t = type_uterm ~expected_ty:olistty ~sr:!sr ~sign:!sign ~ctx t in
1014      let new_obj = {obj with context = Context.normalize [t]} in
1015      delay_mainline ?name
1016        (Obj (new_obj, r))
1017        (Binding(Forall, [("X", oty)],
1018                 Arrow(member (Term.const "X" oty)
1019                         (Context.context_to_term obj.context),
1020                       member (Term.const "X" oty)
1021                         t))) ;
1022  | _ ->
1023      failwith "The monotone command can only be used on hypotheses of the form {...}"
1024
1025
1026(* Theorem *)
1027
1028let theorem thm =
1029  sequent.goal <- thm
1030
1031
1032(* Introduction of forall variables *)
1033
1034let intros hs =
1035  let rec aux hs term =
1036    match term with
1037    | Binding(Forall, bindings, body) ->
1038        let support = metaterm_support body in
1039        let (alist, vars) =
1040          fresh_raised_alist ~sr:!sr ~tag:Eigen ~used:sequent.vars
1041            ~support bindings
1042        in
1043        List.iter add_var (List.map term_to_pair vars) ;
1044        aux hs (replace_metaterm_vars alist body)
1045    | Binding(Nabla, bindings, body) ->
1046        let (ids, tys) = List.split bindings in
1047        aux hs (replace_metaterm_vars
1048                  (List.combine ids (fresh_nominals tys body))
1049                  body)
1050    | Arrow(left, right) -> begin
1051        let (name, hs) = match hs with
1052          | [] -> (None, [])
1053          | "_" :: hs -> (None, hs)
1054          | h :: hs -> (Some h, hs)
1055        in
1056        add_hyp ?name (normalize left) ;
1057        aux hs right
1058      end
1059    | _ -> term
1060  in
1061  sequent.goal <- aux hs sequent.goal
1062
1063(* Split *)
1064
1065let split ?name propogate_result =
1066  let rec accum_goals conjs prev =
1067    match conjs with
1068    | [] -> []
1069    | g::rest ->
1070        let saved = goal_to_subgoal g in
1071        let subgoal () =
1072          saved () ;
1073          if propogate_result then
1074            List.iter (add_hyp ?name) (List.rev prev)
1075        in
1076        subgoal :: (accum_goals rest (g :: prev))
1077  in
1078  let conjs = and_to_list sequent.goal in
1079  if List.length conjs = 1 then failwith "Needless use of split" ;
1080  add_subgoals (accum_goals conjs []) ;
1081  next_subgoal ()
1082
1083(* Split theorem *)
1084
1085let decompose_forall_nabla t =
1086  match t with
1087  | Binding(Forall, foralls, Binding(Nabla, nablas, body)) ->
1088      (foralls, nablas, body)
1089  | Binding(Forall, foralls, body) ->
1090      (foralls, [], body)
1091  | Binding(Nabla, nablas, body) ->
1092      ([], nablas, body)
1093  | _ ->
1094      ([], [], t)
1095
1096let multiarrow arrows body =
1097  let rec aux = function
1098    | h::hs -> Arrow(h, aux hs)
1099    | [] -> body
1100  in
1101  aux arrows
1102
1103let ensure_no_renaming vars terms =
1104  let conflicts =
1105    List.intersect
1106      vars
1107      (List.map fst (all_tids (List.flatten_map collect_terms terms)))
1108  in
1109  if conflicts <> [] then
1110    bugf "Variable renaming required"
1111
1112let split_theorem (tys, thm) =
1113  let foralls, nablas, body = decompose_forall_nabla thm in
1114  let arrows, conj = decompose_arrow body in
1115  let nabla_consts = List.map (fun (x, ty) -> const x ty) nablas in
1116  let lift t =
1117    let iforalls, inablas, ibody = decompose_forall_nabla t in
1118    (* Raise iforalls over nablas *)
1119    let alist, iforall_vars =
1120      fresh_raised_alist ~used:[] ~sr:!sr ~tag:Constant
1121        ~support:nabla_consts iforalls
1122    in
1123    let iforalls = List.map (fun x -> (term_to_name x, tc [] x)) iforall_vars in
1124    let ibody = replace_metaterm_vars alist ibody in
1125    ensure_no_renaming (List.map fst (iforalls @ inablas)) arrows ;
1126    let thm = forall (foralls @ iforalls)
1127        (nabla (nablas @ inablas)
1128           (multiarrow arrows ibody)) in
1129    (tys, thm)
1130  in
1131  List.map lift (and_to_list conj)
1132
1133let create_split_theorems name names =
1134  let thms = split_theorem (get_generic_lemma name) in
1135  let rec loop = function
1136    | n::ns, t::ts, count ->
1137        (n, t) :: (loop (ns, ts, count+1))
1138    | [], t::ts, count ->
1139        (name ^ (string_of_int count), t) :: (loop ([], ts, count+1))
1140    | _ -> []
1141  in
1142  if List.length thms = 1 then
1143    failwith "Cannot split this type of theorem" ;
1144  loop (names, thms, 1)
1145
1146(* Left and right side of disjunction *)
1147
1148let left () =
1149  match sequent.goal with
1150  | Or(left, _) -> sequent.goal <- left
1151  | _ -> ()
1152
1153let right () =
1154  match sequent.goal with
1155  | Or(_, right) -> sequent.goal <- right
1156  | _ -> ()
1157
1158
1159(* Unfold *)
1160
1161let unfold clause_sel sol_sel =
1162  let mdefs = def_unfold sequent.goal in
1163  let used = sequent.vars in
1164  let goal = unfold ~used ~mdefs clause_sel sol_sel sequent.goal in
1165  let goals = List.concat (List.map and_to_list goal) in
1166  add_subgoals (List.map goal_to_subgoal goals) ;
1167  next_subgoal ()
1168
1169(* Exists *)
1170
1171let rec resolve_ewitness ew tids =
1172  match ew, tids with
1173  | ETerm t, ((id, ty) :: tids) ->
1174      (id, ty, t, tids)
1175  | ESub (x, t), ((id, ty) :: tids) ->
1176      if x = id then (x, ty, t, tids) else
1177      let (_, xty, _, tids) = resolve_ewitness ew tids in
1178      (x, xty, t, (id, ty) :: tids)
1179  | _, [] ->
1180      failwithf "Could not resolve existential witness %s" (ewitness_to_string ew)
1181
1182let exists ew =
1183  match sequent.goal with
1184  | Binding (Metaterm.Exists, tids, body) ->
1185      let (id, ty, t, tids) = resolve_ewitness ew tids in
1186      let ntids = metaterm_nominal_tids body in
1187      let ctx = sequent.vars @
1188                (List.map (fun (id, ty) -> (id, nominal_var id ty)) ntids)
1189      in
1190      let t = type_uterm ~expected_ty:ty ~sr:!sr ~sign:!sign ~ctx t in
1191      let goal = replace_metaterm_vars [(id, t)] (exists tids body) in
1192      sequent.goal <- goal ;
1193      normalize_sequent ()
1194  | _ -> ()
1195
1196(* Skip *)
1197
1198let skip () =
1199  next_subgoal ()
1200
1201(* Clear *)
1202
1203let remove_var cm h =
1204  let v = term_to_var (List.assoc h sequent.vars) in
1205  sequent.vars <- List.filter (fun xv -> fst xv <> h) sequent.vars ;
1206  match cm with
1207  | Clear_extro ->
1208      let goal = replace_metaterm_vars [h, var Constant v.Term.name v.ts v.ty] sequent.goal in
1209      sequent.goal <- forall [v.Term.name, v.ty] goal
1210  | _ -> ()
1211
1212let remove_thing cm h =
1213  if is_hyp h
1214  then remove_hyp cm h
1215  else remove_var cm h
1216
1217let is_used v =
1218  List.exists begin fun h ->
1219    get_metaterm_used h.term |>
1220    List.exists (fun xv -> fst xv = v)
1221  end sequent.hyps
1222
1223let check_removable h =
1224  if not (is_hyp h) then
1225    try
1226      let v = List.assoc h sequent.vars in
1227      if is_uninstantiated (h, v) && is_used h then
1228        failwithf "Cannot clear strict uninstantiated variable %s" h
1229    with Not_found ->
1230      failwithf "Unknown hypothesis or variable %s" h
1231
1232let clear cm hs =
1233  List.iter begin fun h ->
1234    check_removable h ;
1235    remove_thing cm h ;
1236  end hs
1237
1238(* Abbrev *)
1239
1240let abbrev ids str =
1241  sequent.hyps <-
1242    List.map begin fun h ->
1243      if Iset.mem h.id ids then
1244        {h with abbrev = Some str}
1245      else h
1246    end sequent.hyps
1247
1248let unabbrev ids =
1249  sequent.hyps <-
1250    List.map begin fun h ->
1251      if Iset.mem h.id ids then
1252        {h with abbrev = None}
1253      else h
1254    end sequent.hyps
1255
1256(* Rename *)
1257
1258let rename_hyp hfr hto =
1259  let hyps = List.map begin
1260      fun h ->
1261        if h.id = hfr then
1262          { h with id = hto }
1263        else h
1264    end sequent.hyps
1265  in sequent.hyps <- hyps
1266
1267let rename_var vfr xto =
1268  let ty = tc [] (List.assoc vfr sequent.vars) in
1269  let vto = var Eigen xto 0 ty in
1270  let repl = [vfr, vto] in
1271  let rewrite mt = replace_metaterm_vars repl mt in
1272  sequent.hyps <-
1273    List.map (fun h -> { h with term = rewrite h.term }) sequent.hyps ;
1274  sequent.vars <-
1275    List.filter_map begin fun v ->
1276      let (x, _) = v in
1277      if x = vfr then Some (xto, vto)
1278      else if x = xto then None
1279      else Some v
1280    end sequent.vars ;
1281  sequent.goal <- rewrite sequent.goal
1282
1283let var_unavailable x =
1284  try is_uninstantiated (x, List.assoc x sequent.vars)
1285  with Not_found -> false
1286
1287let rename xfr xto =
1288  if H.mem lemmas xto then
1289    failwithf "%S already refers to a lemma" xto ;
1290  if List.exists (fun h -> h.id = xto) sequent.hyps then
1291    failwithf "%S already refers to an existing hypothesis" xto ;
1292  if var_unavailable xto then
1293    failwithf "%S already refers to an existing variable" xto ;
1294  if List.exists (fun h -> h.id = xfr) sequent.hyps then
1295    rename_hyp xfr xto
1296  else if List.mem_assoc xfr sequent.vars then
1297    rename_var xfr xto
1298  else
1299    failwithf "Unknown variable or hypothesis label %S" xfr
1300
1301(* Permute *)
1302
1303let permute_nominals ids form =
1304  if not (List.is_unique ids) then failwith "Not a permutation" ;
1305  let term =
1306    match form with
1307    | None -> sequent.goal
1308    | Some h -> get_hyp h
1309  in
1310  let support_alist =
1311    List.map (fun t -> (term_to_name t, t)) (metaterm_support term)
1312  in
1313  let perm =
1314    List.map
1315      (fun id ->
1316         try
1317           List.assoc id support_alist
1318         with Not_found -> nominal_var id (tybase (atybase "")))
1319      ids
1320  in
1321  let result = Tactics.permute_nominals perm term in
1322  match form with
1323  | None -> sequent.goal <- result
1324  | Some hyp -> replace_hyp hyp result
1325
1326(* Object level cut with explicit cut formula*)
1327
1328let cut_from ?name h arg term =
1329  let term = type_uterm ~sr:!sr ~sign:!sign ~ctx:sequent.vars term in
1330  let h = get_stmt_clearly h in
1331  let arg = get_stmt_clearly arg in
1332  match h, arg with
1333  | Obj(obj_h1, _),Obj(obj_h2, _) ->
1334      add_hyp ?name (object_cut_from obj_h1 obj_h2 term)
1335  | _,_ -> failwith "The cut command can only be used on \
1336                   \ hypotheses of the form {...}"
1337