diff options
-rw-r--r-- | tactics/tacticals.ml | 4 | ||||
-rw-r--r-- | tactics/tacticals.mli | 3 | ||||
-rw-r--r-- | tactics/tactics.ml | 1694 | ||||
-rw-r--r-- | tactics/tactics.mli | 244 |
4 files changed, 1942 insertions, 3 deletions
diff --git a/tactics/tacticals.ml b/tactics/tacticals.ml index 1794755ff..56cd5a42f 100644 --- a/tactics/tacticals.ml +++ b/tactics/tacticals.ml @@ -413,7 +413,7 @@ let make_elim_branch_assumptions ba gl = | (_, _) -> error "make_elim_branch_assumptions" in makerec ([],[],[],[],[]) ba.branchsign - (try firstn ba.nassums (ids_of_sign (pf_untyped_hyps gl)) + (try list_firstn ba.nassums (ids_of_sign (pf_untyped_hyps gl)) with Failure _ -> anomaly "make_elim_branch_assumptions") let elim_on_ba tac ba gl = tac (make_elim_branch_assumptions ba gl) gl @@ -441,7 +441,7 @@ let make_case_branch_assumptions ba gl = | (_, _) -> error "make_case_branch_assumptions" in makerec ([],[],[],[]) ba.branchsign - (try firstn ba.nassums (ids_of_sign (pf_untyped_hyps gl)) + (try list_firstn ba.nassums (ids_of_sign (pf_untyped_hyps gl)) with Failure _ -> anomaly "make_case_branch_assumptions") let case_on_ba tac ba gl = tac (make_case_branch_assumptions ba gl) gl diff --git a/tactics/tacticals.mli b/tactics/tacticals.mli index 1ebd64f43..216a24b31 100644 --- a/tactics/tacticals.mli +++ b/tactics/tacticals.mli @@ -101,7 +101,8 @@ type branch_assumptions = { val sort_of_goal : goal sigma -> sorts val suff : goal sigma -> constr -> string -val lookup_eliminator : context -> section_path -> string -> constr +val lookup_eliminator : + typed_type signature -> section_path -> string -> constr val general_elim_then_using : constr -> (constr -> int -> bool list) -> diff --git a/tactics/tactics.ml b/tactics/tactics.ml new file mode 100644 index 000000000..8bf647a69 --- /dev/null +++ b/tactics/tactics.ml @@ -0,0 +1,1694 @@ + +(* $Id$ *) + +open Pp +open Util +open Names +open Sign +open Generic +open Term +open Inductive +open Reduction +open Environ +open Evd +open Pfedit +open Tacred +open Tacmach +open Proof_trees +open Logic +open Clenv +open Tacticals +open Pattern + +exception Bound + +(*********************************************) +(* Tactics *) +(*********************************************) + +(****************************************) +(* General functions *) +(****************************************) + +let get_pairs_from_bindings = + let pair_from_binding = function + | [(Bindings binds)] -> binds + | _ -> error "not a binding list!" + in + List.map pair_from_binding + +let get_commands = + let command_from_tacarg = function + | (Command x) -> x + | _ -> error "get_commands: not a command!" + in + List.map command_from_tacarg + +let rec string_head_bound = function + | DOPN(Const _,_) as x -> + string_of_id (basename (path_of_const x)) + | DOPN(MutInd _,_) as x -> + let mispec = Global.lookup_mind_specif x in + string_of_id (mis_typename mispec) + | DOPN(MutConstruct ((sp,tyi),i),_) -> + let mib = Global.lookup_mind sp in + string_of_id (mib.mind_packets.(tyi).mind_consnames.(i-1)) + | VAR id -> string_of_id id + | _ -> raise Bound + +let string_head c = + try string_head_bound c with Bound -> error "Bound head variable" + +let rec head_constr_bound t l = + let t = strip_outer_cast(collapse_appl t) in + match t with + | DOP2(Prod,_,DLAM(_,c2)) -> head_constr_bound c2 l + | DOPN(AppL,cl) -> + head_constr_bound (array_hd cl) ((List.tl (Array.to_list cl))@l) + | DOPN(Const _,_) as x -> x::l + | DOPN(MutInd _,_) as x -> x::l + | DOPN(MutConstruct _,_) as x -> x::l + | VAR _ as x -> x::l + | _ -> raise Bound + +let head_constr c = + try head_constr_bound c [] with Bound -> error "Bound head variable" + +let bad_tactic_args s l = + raise (RefinerError (BadTacticArgs (s,l))) + +(******************************************) +(* Primitive tactics *) +(******************************************) + +let introduction = Tacmach.introduction +let intro_replacing = Tacmach.intro_replacing +let refine = Tacmach.refine +let convert_concl = Tacmach.convert_concl +let convert_hyp = Tacmach.convert_hyp +let thin = Tacmach.thin +let move_hyp = Tacmach.move_hyp + +let mutual_fix = Tacmach.mutual_fix +let fix f n = mutual_fix [f] [n] [] + +let fix_noname n = + let l = Pfedit.list_proofs() in + let id = id_of_string (List.hd l) in + fix id n + +(***TODO: les versions dyn_ +let dyn_mutual_fix argsl gl = + match argsl with + | [Integer n] -> fix_noname n gl + | [Identifier id;Integer n] -> fix id n gl + | ((Identifier id)::(Integer n)::lfix) -> + let rec decomp lid ln lar = function + | (Fixexp (id,n,ar)::rest) -> + decomp (id::lid) (n::ln) (ar::lar) rest + | [] -> (List.rev lid,List.rev ln,List.rev lar) + | _ -> bad_tactic_args "mutual_fix" argsl + in + let (lid,ln,lar) = decomp [] [] [] lfix in + mutual_fix (id::lid) (n::ln) (List.map (pf_constr_of_com gl) lar) gl + | l -> bad_tactic_args "mutual_fix" l +***) + +let mutual_cofix = Tacmach.mutual_cofix +let cofix f = mutual_cofix [f] [] + +let cofix_noname n = + let l = Pfedit.list_proofs() in + let id = id_of_string (List.hd l) in + cofix id n + +(***TODO +let dyn_mutual_cofix argsl gl = + match argsl with + | [] -> cofix_noname gl + | [(Identifier id)] -> cofix id gl + | ((Identifier id)::lcofix) -> + let rec decomp lid lar = function + | (Cofixexp (id,ar)::rest) -> + decomp (id::lid) (ar::lar) rest + | [] -> (List.rev lid,List.rev lar) + | _ -> bad_tactic_args "mutual_cofix" argsl + in + let (lid,lar) = decomp [] [] lcofix in + mutual_cofix (id::lid) (List.map (pf_constr_of_com gl) lar) gl + | l -> bad_tactic_args "mutual_cofix" l +***) + +(**************************************************************) +(* Reduction and conversion tactics *) +(**************************************************************) + +(* The following two tactics apply an arbitrary + reduction function either to the conclusion or to a + certain hypothesis *) + +let reduct_in_concl redfun gl = + convert_concl (pf_reduce redfun gl (pf_concl gl)) gl + +let reduct_in_hyp redfun id gl = + let ty = pf_get_hyp gl id in + let redfun' = under_casts (fun _ _ -> pf_reduce redfun gl) in + convert_hyp id (redfun' (pf_env gl) (project gl) ty) gl + +let reduct_option redfun = function + | Some id -> reduct_in_hyp redfun id + | None -> reduct_in_concl redfun + +(* The following tactic determines whether the reduction + function has to be applied to the conclusion or + to the hypotheses. *) + +let in_combinator tac1 tac2 = function + | [] -> tac1 + | x -> (tclMAP tac2 x) + +let redin_combinator redfun = function + | [] -> reduct_in_concl redfun + | x -> (tclMAP (reduct_in_hyp redfun) x) + + +(* Now we introduce different instances of the previous tacticals *) +let change_hyp_and_check t env sigma c = + if is_conv (Global.unsafe_env()) sigma t c then + t + else + errorlabstrm "convert-check-hyp" [< 'sTR "Not convertible" >] + +let change_concl_and_check t env sigma c = + if is_conv_leq (Global.unsafe_env()) sigma t c then + t + else + errorlabstrm "convert-check-concl" [< 'sTR "Not convertible" >] + +let change_in_concl t = reduct_in_concl (change_concl_and_check t) +let change_in_hyp t = reduct_in_hyp (change_hyp_and_check t) + +let change_option t = function + | Some id -> reduct_in_hyp (change_hyp_and_check t) id + | None -> reduct_in_concl (change_concl_and_check t) + +(* Pour usage interne (le niveau User est pris en compte par dyn_reduce) *) +let red_in_concl = reduct_in_concl red_product +let red_in_hyp = reduct_in_hyp red_product +let red_option = reduct_option red_product +let hnf_in_concl = reduct_in_concl hnf_constr +let hnf_in_hyp = reduct_in_hyp hnf_constr +let hnf_option = reduct_option hnf_constr +let simpl_in_concl = reduct_in_concl nf +let simpl_in_hyp = reduct_in_hyp nf +let simpl_option = reduct_option nf +let normalise_in_concl = reduct_in_concl compute +let normalise_in_hyp = reduct_in_hyp compute +let normalise_option = reduct_option compute +let unfold_in_concl loccname = reduct_in_concl (unfoldn loccname) +let unfold_in_hyp loccname = reduct_in_hyp (unfoldn loccname) +let unfold_option loccname = reduct_option (unfoldn loccname) +let pattern_option l = reduct_option (pattern_occs l) + +(*** +let dyn_change = function + | [Command (com); Clause cl] -> + (fun goal -> + let c = constr_of_com_sort (project goal) (pf_hyps goal) com in + in_combinator (change_in_concl c) (change_in_hyp c) cl goal) + | l -> bad_tactic_args "change" l +***) + +(* A function which reduces accordingly to a reduction expression, + as the command Eval does. *) + +let reduce redexp cl goal = + redin_combinator (reduction_of_redexp redexp) cl goal + +(*** +let dyn_reduce = function + | [Redexp (redn,args); Clause cl] -> + (fun goal -> + let redexp = + redexp_of_ast (project goal) (pf_hyps goal) (redn,args) in + reduce redexp cl goal) + | l -> bad_tactic_args "reduce" l +***) + +(* Unfolding occurrences of a constant *) + +let unfold_constr c = + match strip_outer_cast c with + | DOPN(Const(sp),_) -> + unfold_in_concl [[],sp] + | t -> + errorlabstrm "unfold_constr" + [< 'sTR "Cannot unfold a non-constant." >] + +(*******************************************) +(* Introduction tactics *) +(*******************************************) + +let next_global_ident_from id avoid = + let rec next_rec id = + let id = next_ident_away_from id avoid in + if not (Declare.is_global id) then + id + else + next_rec (lift_ident id) + in + next_rec id + +let next_global_ident_away id avoid = + let id = next_ident_away id avoid in + if not (Declare.is_global id) then + id + else + next_global_ident_from (lift_ident id) avoid + +let fresh_id avoid id gl = + next_global_ident_away id (avoid@(ids_of_sign (pf_untyped_hyps gl))) + +let id_of_name_with_default s = function + | Anonymous -> id_of_string s + | Name id -> id + +let default_id gl = + match strip_outer_cast (pf_concl gl) with + | DOP2(Prod,c1,DLAM(name,c2)) -> + (match pf_whd_betadeltaiota gl (pf_type_of gl c1) with + | DOP0(Sort(Prop _)) -> (id_of_name_with_default "H" name) + | DOP0(Sort(Type(_))) -> (id_of_name_with_default "X" name) + | _ -> anomaly "Wrong sort") + | _ -> error "Introduction needs a product" + +(* Non primitive introduction tactics are treated by central_intro + There is possibly renaming, with possibly names to avoid and + possibly a move to do after the introduction *) + +type intro_name_flag = + | IAvoid of identifier list + | IBasedOn of identifier * identifier list + | IMustBe of identifier + +let rec central_intro name_flag move_flag force_flag gl = + try + let id = + match name_flag with + | IAvoid idl -> fresh_id idl (default_id gl) gl + | IBasedOn (id,idl) -> fresh_id idl id gl + | IMustBe id -> + let id' = fresh_id [] id gl in + if id' <> id then warning + ((string_of_id id)^ + " is already used; changed to "^(string_of_id id')); + id' + in + begin match move_flag with + | None -> introduction id gl + | Some dest -> tclTHEN (introduction id) (move_hyp true id dest) gl + end + with (UserError ("Introduction needs a product",_)) as e -> + if force_flag then + try + ((tclTHEN (reduce Red []) (central_intro name_flag move_flag + force_flag)) gl) + with UserError ("Term not reducible",_) -> + errorlabstrm "Intro" + [<'sTR "No product even after head-reduction">] + else + raise e + +let intro_using_warning id = central_intro (IMustBe id) None false +let intro_using id = central_intro (IBasedOn (id,[])) None false +let intro_force force_flag = central_intro (IAvoid []) None force_flag +let intro = intro_force false +let introf = intro_force true + +(**** Multiple introduction tactics ****) + +let rec intros_using = function + [] -> tclIDTAC + | str::l -> tclTHEN (intro_using str) (intros_using l) + +let intros = tclREPEAT (intro_force false) + +let intro_erasing id = tclTHEN (thin [id]) (intro_using id) + +(* User-level introduction tactics *) + +let dyn_intro = function + | [] -> central_intro (IAvoid []) None true + | [Identifier id] -> central_intro (IMustBe id) None true + | l -> bad_tactic_args "intro" l + +let dyn_intro_move = function + | [Identifier id2] -> central_intro (IAvoid []) (Some id2) true + | [Identifier id; Identifier id2] -> + central_intro (IMustBe id) (Some id2) true + | l -> bad_tactic_args "intro_move" l + +let rec intros_until s g = + match pf_lookup_name_as_renamed (pf_hyps g) (pf_concl g) s with + | Some depth -> tclDO depth intro g + | None -> + try + ((tclTHEN (reduce Red []) (intros_until s)) g) + with UserError ("Term not reducible",_) -> + errorlabstrm "Intros" + [<'sTR "No such hypothesis in current goal"; + 'sTR "even after head-reduction" >] + +let rec intros_until_n n g = + match pf_lookup_index_as_renamed (pf_concl g) n with + | Some depth -> tclDO depth intro g + | None -> + try + ((tclTHEN (reduce Red []) (intros_until_n n)) g) + with UserError ("Term not reducible",_) -> + errorlabstrm "Intros" + [<'sTR "No such hypothesis in current goal"; + 'sTR "even after head-reduction" >] + +let dyn_intros_until = function + | [Identifier id] -> intros_until id + | [Integer n] -> intros_until_n n + | l -> bad_tactic_args "Intros until" l + +let intros_do n g = + let depth = + let rec lookup all nodep = function + | DOP2(Prod,_,DLAM(name,c')) -> + (match name with + | Name(s') -> + if dependent (Rel 1) c' then + lookup (all+1) nodep c' + else if nodep = n then + all + else + lookup (all+1) (nodep+1) c' + | Anonymous -> + if nodep=n then all else lookup (all+1) (nodep+1) c') + | DOP2(Cast,c,_) -> lookup all nodep c + | _ -> error "No such hypothesis in current goal" + in + lookup 1 1 (pf_concl g) + in + tclDO depth intro g + +let rec intros_move = function + | [] -> tclIDTAC + | (hyp,destopt) :: rest -> + tclTHEN (central_intro (IMustBe hyp) destopt false) + (intros_move rest) + +let move_to_rhyp rhyp gl = + let rec get_lhyp lastfixed deptyp = function + | [] -> + (match rhyp with + | None -> lastfixed + | Some h -> anomaly ("Hypothesis should occur: "^ (string_of_id h))) + | (hyp,typ) as ht :: rest -> + if Some hyp = rhyp then + lastfixed + else if List.exists (occur_var hyp) deptyp then + get_lhyp lastfixed (typ::deptyp) rest + else + get_lhyp (Some hyp) deptyp rest + in + let sign = pf_untyped_hyps gl in + let (hyp,typ) = hd_sign sign in + match get_lhyp None [typ] (list_of_sign sign) with + | None -> tclIDTAC gl + | Some hypto -> move_hyp true hyp hypto gl + +let rec intros_rmove = function + | [] -> tclIDTAC + | (hyp,destopt) :: rest -> + tclTHENLIST [ introduction hyp; + move_to_rhyp destopt; + intros_rmove rest ] + +(****************************************************) +(* Resolution tactics *) +(****************************************************) + +(* Refinement tactic: unification with the head of the head normal form + * of the type of a term. *) + +let apply_type hdcty argl gl = + refine (DOPN(AppL,Array.of_list + ((DOP2(Cast,DOP0(Meta(newMETA())),hdcty))::argl))) gl + +let apply_term hdc argl gl = + refine (DOPN(AppL,Array.of_list(hdc::argl))) gl + +let bring_hyps clsl gl = + let ids = + List.map + (function + | (Some id) -> id + | None -> error "BringHyps") clsl in + let newcl = + List.fold_right + (fun id cl' -> mkNamedProd id (pf_type_of gl (VAR id)) cl') + ids (pf_concl gl) + in + apply_type newcl (List.map (fun id -> VAR id) ids) gl + +(* Resolution with missing arguments *) + +let collect_com lbind = + map_succeed (function (Com,c)->c | _ -> failwith "Com") lbind + +let make_clenv_binding_apply wc (c,t) lbind = + let largs = collect_com lbind in + let lcomargs = List.length largs in + if lcomargs = List.length lbind then + let clause = mk_clenv_from wc (c,t) in + clenv_constrain_missing_args largs clause + else if lcomargs = 0 then + let clause = mk_clenv_rename_from wc (c,t) in + clenv_match_args lbind clause + else + errorlabstrm "make_clenv_bindings" + [<'sTR "Cannot mix bindings and free associations">] + +let apply_with_bindings (c,lbind) gl = + let (wc,kONT) = startWalk gl in + let t = w_hnf_constr wc (w_type_of wc c) in + let clause = make_clenv_binding_apply wc (c,t) lbind in + let apply = + match c with + | DOP2(Lambda,_,_) -> res_pf_cast + | _ -> res_pf + in + apply kONT clause gl + +let apply c = apply_with_bindings (c,[]) +let apply_com = tactic_com (fun c -> apply_with_bindings (c,[])) + +let apply_list = function + | c::l -> apply_with_bindings (c,List.map (fun com ->(Com,com)) l) + | _ -> assert false + +(* Resolution with no reduction on the type *) + +let apply_without_reduce c gl = + let (wc,kONT) = startWalk gl in + let clause = mk_clenv_type_of wc c in + res_pf kONT clause gl + +let apply_without_reduce_com = tactic_com apply_without_reduce + +let refinew_scheme kONT clause gl = res_pf kONT clause gl + +let dyn_apply l = + match l with + | [Command com; BINDINGS binds] -> + tactic_com_bind_list apply_with_bindings (com,binds) + | [CONSTR c; CBINDINGS binds] -> + apply_with_bindings (c,binds) + | l -> + bad_tactic_args "apply" l + +(* A useful resolution tactic, equivalent to Cut type_of_c;[Idtac|Apply c] *) + +let cut_and_apply c gl = + let goal_constr = pf_concl gl in + match (pf_hnf_constr gl (pf_type_of gl c)) with + | DOP2(Prod,c1,DLAM(_,c2)) when not (dependent (Rel 1) c2) -> + tclTHENS + (apply_type (DOP2(Prod,c2,DLAM(Anonymous,goal_constr))) + [DOP0(Meta(newMETA()))]) + [tclIDTAC;apply_term c [DOP0(Meta(newMETA()))]] gl + | _ -> error "Imp_elim needs a non-dependent product" + +let dyn_cut_and_apply = function + | [Command com] -> tactic_com cut_and_apply com + | [Constr c] -> cut_and_apply c + | l -> bad_tactic_args "cut_and_apply" l + +(**************************) +(* Cut tactics *) +(**************************) + +let cut c gl = + match hnf_type_of gl c with + | (DOP0(Sort _)) -> + apply_type (DOP2(Prod,c,DLAM(Anonymous,(pf_concl gl)))) + [DOP0(Meta (newMETA()))] gl + | _ -> error "Not a proposition or a type" + +let dyn_cut = function + | [Command com] -> tactic_com_sort cut com + | [Constr c] -> cut c + | l -> bad_tactic_args "cut" l + +let cut_intro t = (tclTHENS (cut t) [intro;tclIDTAC]) + +let cut_replacing id t = + (tclTHENS (cut t) + [(tclORELSE (intro_replacing id) + (tclORELSE (intro_erasing id) + (intro_using id))); + tclIDTAC]) + +let cut_in_parallel l = + let rec prec = function + | [] -> tclIDTAC + | h::t -> (tclTHENS (cut h) ([prec t;tclIDTAC])) + in + prec (List.rev l) + +(**************************) +(* Generalize tactics *) +(**************************) + +let generalize_goal gl c cl = + let t = pf_type_of gl c in + match c with + | (VAR id) -> mkNamedProd id t cl + | _ -> + let cl' = subst_term c cl in + if noccurn 1 cl' then + DOP2(Prod,t,DLAM(Anonymous,cl)) + (* On ne se casse pas la tete : on prend pour nom de variable + la premiere lettre du type, meme si "ci" est une + constante et qu'on pourrait prendre directement son nom *) + else + prod_name (Anonymous, t, cl') + +let generalize_dep c gl = + let sign = pf_untyped_hyps gl in + let init_ids = ids_of_sign (initial_sign ()) in + let rec seek ((hl,tl) as toquant) h t = + if List.exists (fun id -> occur_var id t) hl or dependent c t then + (h::hl,t::tl) + else + toquant + in + let (hl,tl) = it_sign seek ([],[]) sign in + let tothin = filter (fun id -> not (List.mem id init_ids)) hl in + let tothin' = + match c with + | VAR id when mem_sign sign id & not (List.mem id init_ids) -> id::tothin + | _ -> tothin + in + let cl' = List.fold_right2 mkNamedProd hl tl (pf_concl gl) in + let cl'' = generalize_goal gl c cl' in + tclTHEN + (apply_type cl'' (c::(List.map (fun id -> VAR id) hl))) + (thin (List.rev tothin')) + gl + +let generalize lconstr gl = + let newcl = List.fold_right (generalize_goal gl) lconstr (pf_concl gl) in + apply_type newcl lconstr gl + +let dyn_generalize = + fun argsl -> tactic_com_list generalize (get_commands argsl) + +let dyn_generalize_dep = function + | [Command com] -> tactic_com generalize_dep com + | l -> bad_tactic_args "dyn_generalize_dep" l + +(* Faudra-t-il une version avec plusieurs args de generalize_dep ? +Cela peut-être troublant de faire "Generalize Dependent H n" dans +"n:nat; H:n=n |- P(n)" et d'échouer parce que H a disparu après la +généralisation dépendante par n. + +let quantify lconstr = + List.fold_right + (fun com tac -> tclTHEN tac (tactic_com generalize_dep c)) + lconstr + tclIDTAC +*) + +(* A dependent cut rule à la sequent calculus *) + +(* Sera simplifiable le jour où il y aura un let in primitif dans constr *) + +(* if [occhypl] is empty, [c] is substituted in every hyp where it occurs *) +(* if name = Anonymous, the name is build from the first letter of the type *) + +let letin_abstract id c occ_ccl occhypl gl = + let allhyp = occhypl=[] in + let hyps = pf_untyped_hyps gl in + let abstract ((dephyps,deptyps,marks,occl as accu),lhyp) hyp typ = + try + let occ = if allhyp then [] else List.assoc hyp occl in + let newtyp = subst1 (VAR id) (subst_term_occ occ c typ) in + let newoccl = except_assoc hyp occl in + if typ=newtyp then + (accu,Some hyp) + else + ((hyp::dephyps,newtyp::deptyps,(hyp,lhyp)::marks,newoccl),lhyp) + with Not_found -> + (accu,Some hyp) + in + let (dephyps,deptyps,marks,rest),_ = + it_sign abstract (([],[],[],occhypl),None) hyps in + if rest <> [] then begin + let id = fst (List.hd rest) in + if mem_sign hyps id + then error ("Hypothesis "^(string_of_id id)^" occurs twice") + else error ("No such hypothesis : " ^ (string_of_id id)) + end; + let ccl = match occ_ccl with + | None -> (pf_concl gl) + | Some occ -> subst1 (VAR id) (subst_term_occ occ c (pf_concl gl)) + in + (dephyps,deptyps,marks,ccl) + +let letin_tac with_eq name c occ_ccl occhypl gl = + let x = id_of_name_using_hdchar (pf_type_of gl c) name in + let hyps = pf_untyped_hyps gl in + let id = next_global_ident_away x (ids_of_sign hyps) in + if mem_sign hyps id then error "New variable is already declared"; + let (dephyps,deptyps,marks,ccl)= letin_abstract id c occ_ccl occhypl gl in + let t = pf_type_of gl c in + let (eqc,reflc) = + let sort = pf_type_of gl t in + if is_Set sort then + (pf_parse_const gl "eq", pf_parse_const gl "refl_equal") + else if is_Type sort then + (pf_parse_const gl "eqT", pf_parse_const gl "refl_eqT") + else error "Not possible with proofs yet" + in + let heq = next_global_ident_away (id_of_string "Heq") (ids_of_sign hyps) in + let tmpcl = List.fold_right2 mkNamedProd dephyps deptyps ccl in + let tmpargs = List.map (fun id -> VAR id) dephyps in + let newcl,args = + if with_eq then + let eq = applist (eqc,[t;VAR id;c]) in + let refl = applist (reflc,[t;c]) in + (mkNamedProd id t (mkNamedProd heq eq tmpcl), c::refl::tmpargs) + else + (mkNamedProd id t tmpcl, c::tmpargs) + in + let lastlhyp = if marks=[] then None else snd (List.hd marks) in + tclTHENLIST + [ apply_type newcl args; + thin dephyps; + central_intro (IMustBe id) lastlhyp false; + if with_eq then central_intro (IMustBe heq) lastlhyp false else tclIDTAC; + intros_move marks ] gl + +let dyn_lettac args gl = match args with + | [Identifier id; Command com; Letpatterns (o,l)] -> + letin_tac true (Name id) (pf_constr_of_com gl com) o l gl + | [Identifier id; Constr c; Letpatterns (o,l)] -> + letin_tac true (Name id) c o l gl + | l -> bad_tactic_args "letin" l + + +(********************************************************************) +(* Exact tactics *) +(********************************************************************) + +let exact c gl = + let concl = (pf_concl gl) in + let ct = pf_type_of gl c in + if pf_conv_x_leq gl ct concl then + refine c gl + else + error "Not an exact proof" + +(* +let dyn_exact = + function [(COMMAND com)] -> tactic_com exact com + | l -> bad_tactic_args "exact" l + +*) + +let dyn_exact cc gl = match cc with + | [Constr c] -> exact c gl + | [Command com] -> + let evc = (project gl) in + let concl = (pf_concl gl) in + let c = constr_of_com_casted evc (pf_hyps gl) com concl in + refine c gl + | l -> bad_tactic_args "exact" l + +let (assumption:tactic) = fun gl -> + let concl = pf_concl gl in + let rec arec = function + | ([],[]) -> error "No such assumption" + | (s::sl,a::al) -> if pf_conv_x_leq gl a concl then + refine (VAR(s)) gl else arec (sl,al) + | _ -> assert false + in + arec (pf_untyped_hyps gl) + +let dyn_assumption = function + | [] -> assumption + | l -> bad_tactic_args "assumption" l + + +(*****************************************************************) +(* Modification of a local context *) +(*****************************************************************) + +(* This tactic enables the user to remove hypotheses from the signature. + * Some care is taken to prevent him from removing variables that are + * subsequently used in other hypotheses or in the conclusion of the + * goal. *) + +let clear ids gl = thin ids gl +let clear_one id gl = clear [id] gl +let dyn_clear = function + | [Clause ids] -> clear ids + | _ -> assert false + +(* Clears a list of identifiers clauses form the context *) + +let clear_clauses clsl = + clear (List.map + (function + | Some id -> id + | None -> error "ThinClauses") clsl) + +(* Clears one identifier clause from the context *) + +let clear_clause cls = clear_clauses [cls] + + +(* Takes a list of booleans, and introduces all the variables + * quantified in the goal which are associated with a value + * true in the boolean list. *) + +let rec intros_clearing = function + | [] -> tclIDTAC + | (false::tl) -> tclTHEN intro (intros_clearing tl) + | (true::tl) -> + tclTHENLIST [ intro; onLastHyp clear_clause; intros_clearing tl] + +(* Adding new hypotheses *) + +let new_hyp mopt c blist g = + let (wc,kONT) = startWalk g in + let clause = mk_clenv_printable_type_of wc c in + let clause' = clenv_match_args blist clause in + let (thd,tstack) = whd_castapp_stack (clenv_instance_template clause')[] in + let nargs = List.length tstack in + let cut_pf = + applist(thd, + match mopt with + | Some m -> if m < nargs then firstn m tstack else tstack + | None -> tstack) + in + (tclTHENL (tclTHEN (kONT clause.hook) + (cut (pf_type_of g cut_pf))) + ((tclORELSE (apply cut_pf) (exact cut_pf)))) g + +let dyn_new_hyp argsl gl = + match argsl with + | [Integer n; Command com; Bindings binds] -> + tactic_bind_list + (new_hyp (Some n) + (pf_constr_of_com gl com)) + binds gl + | [Command com; Bindings binds] -> + tactic_bind_list + (new_hyp None + (pf_constr_of_com gl com)) + binds gl + | [Integer n; Constr c; Cbindings binds] -> + new_hyp (Some n) c binds gl + | [Constr c; Cbindings binds] -> + new_hyp None c binds gl + | l -> bad_tactic_args "new_hyp" l + +(* Moving hypotheses *) + +let dyn_move = function + | [Identifier idfrom; Identifier idto] -> move_hyp false idfrom idto + | _ -> assert false + +let dyn_move_dep = function + | [Identifier idfrom; Identifier idto] -> move_hyp true idfrom idto + | _ -> assert false + +(************************) +(* Introduction tactics *) +(************************) + +let constructor_checking_bound boundopt i lbind gl = + let cl = pf_concl gl in + let (mind,_,redcl) = reduce_to_mind (project gl) cl in + let (x_0,x_1,args) = destMutInd mind in + let nconstr = mis_nconstr (mind_specif_of_mind mind) + and sigma = project gl in + if i=0 then error "The constructors are numbered starting from 1"; + if i > nconstr then error "Not enough constructors"; + begin match boundopt with + | Some expctdnum -> + if expctdnum <> nconstr then + error "Not the expected number of constructors" + | None -> () + end; + let cons = DOPN(MutConstruct((x_0,x_1),i),args) in + let apply_tac = apply_with_bindings (cons,lbind) in + (tclTHENLIST [convert_concl redcl; intros; apply_tac]) gl + +let one_constructor i = (constructor_checking_bound None i) + +let any_constructor gl = + let cl = pf_concl gl in + let (mind,_,redcl) = reduce_to_mind (project gl) cl in + let (x_0,x_1,args) = destMutInd mind in + let nconstr = mis_nconstr (mind_specif_of_mind mind) + and sigma = project gl in + if nconstr = 0 then error "The type has no constructors"; + tclFIRST (List.map (fun i -> one_constructor i []) + (interval 1 nconstr)) gl + +let dyn_constructor = function + | [Integer i; Bindings binds] -> tactic_bind_list (one_constructor i) binds + | [Integer i; Cbindings binds] -> (one_constructor i) binds + | [] -> any_constructor + | l -> bad_tactic_args "constructor" l + +let left = (constructor_checking_bound (Some 2) 1) +let simplest_left = left [] + +let dyn_left = function + | [Cbindings binds] -> left binds + | [Bindings binds] -> tactic_bind_list left binds + | l -> bad_tactic_args "left" l + +let right = (constructor_checking_bound (Some 2) 2) +let simplest_right = right [] + +let dyn_right = function + | [Cbindings binds] -> right binds + | [Bindings binds] -> tactic_bind_list right binds + | l -> bad_tactic_args "right" l + + +let split = (constructor_checking_bound (Some 1) 1) +let simplest_split = split [] + +let dyn_split = function + | [Cbindings binds] -> split binds + | [Bindings binds] -> tactic_bind_list split binds + | l -> bad_tactic_args "split" l + +(********************************************) +(* Elimination tactics *) +(********************************************) + + +(* kONT : ?? + * wc : ?? + * elimclause : ?? + * inclause : ?? + * gl : the current goal +*) + +let last_arg = function + | DOPN(AppL,cl) -> cl.(Array.length cl - 1) + | _ -> anomaly "last_arg" + +let elimination_clause_scheme kONT wc elimclause indclause gl = + let indmv = + (match last_arg (clenv_template elimclause).rebus with + | DOP0(Meta mv) -> mv + | _ -> errorlabstrm "elimination_clause" + [< 'sTR "The type of elimination clause is not well-formed" >]) + in + let elimclause' = clenv_fchain indmv elimclause indclause in + elim_res_pf kONT elimclause' gl + +(* cast added otherwise tactics Case (n1,n2) generates (?f x y) and + * refine fails *) + +let make_clenv_binding wc (c,t) lbind = + let largs = collect_com lbind in + let lcomargs = List.length largs in + if lcomargs = List.length lbind then + let clause = mk_clenv_from wc (c,t) in + clenv_constrain_dep_args largs clause + else if lcomargs = 0 then + let clause = mk_clenv_rename_from wc (c,t) in + clenv_match_args lbind clause + else + errorlabstrm "make_clenv_bindings" + [<'sTR "Cannot mix bindings and free associations">] + +let type_clenv_binding wc (c,t) lbind = + clenv_instance_template_type (make_clenv_binding wc (c,t) lbind) + +(* + * Elimination tactic with bindings and using an arbitrary + * elimination constant called elimc. This constant should end + * with a clause (x:I)(P .. ), where P is a bound variable. + * The term c is of type t, which is a product ending with a type + * matching I, lbindc are the expected terms for c arguments + *) + +let general_elim (c,lbindc) (elimc,lbindelimc) gl = + let (wc,kONT) = startWalk gl in + let (_,_,t) = reduce_to_ind (project gl) (pf_type_of gl c) in + let indclause = make_clenv_binding wc (c,t) lbindc in + let elimt = w_type_of wc elimc in + let elimclause = make_clenv_binding wc (elimc,elimt) lbindelimc in + elimination_clause_scheme kONT wc elimclause indclause gl + +(* Elimination tactic with bindings but using the default elimination + * constant associated with the type. *) + +let default_elim (c,lbindc) gl = + let (path_name,_,t) = reduce_to_ind (project gl) (pf_type_of gl c) in + let elimc = + lookup_eliminator (pf_hyps gl) path_name (suff gl (pf_concl gl)) + in + general_elim (c,lbindc) (elimc,[]) gl + + +(* The simplest elimination tactic, with no substitutions at all. *) + +let simplest_elim c = default_elim (c,[]) + +(* + * A "natural" induction tactic + * + - [H0:T0, ..., Hi:Ti, hyp0:P->I(args), Hi+1:Ti+1, ..., Hn:Tn |-G] is the goal + - [hyp0] is the induction hypothesis + - we extract from [args] the variables which are not rigid parameters + of the inductive type, this is [indvars] (other terms are forgotten); + [indhyps] are the ones which actually are declared in context + (done in [find_atomic_param_of_ind]) + - we look for all hyps depending of [hyp0] or one of [indvars]: + this is [dephyps] of types [deptyps] respectively + - [statuslist] tells for each hyps in [dephyps] after which other hyp + fixed in the context they must be moved (when induction is done) + - [hyp0succ] is the name of the hyp fixed in the context after which to + move the subterms of [hyp0succ] in the i-th branch where it is supposed + to be the i-th constructor of the inductive type. + + Strategy: (cf in [induction_from_context]) + - requantify and clear all [dephyps] + - apply induction on [hyp0] + - clear [indhyps] and [hyp0] + - in the i-th subgoal, intro the arguments of the i-th constructor + of the inductive type after [hyp0succ] (done in + [induct_discharge]) let the induction hypotheses on top of the + hyps because they may depend on variables between [hyp0] and the + top. A counterpart is that the dep hyps programmed to be intro-ed + on top must now be intro-ed after the induction hypotheses + - move each of [dephyps] at the right place following the + [statuslist] + + *) + + +let rec is_rec_arg indpath t = + try + mind_path (fst (find_mrectype empty_evd t)) = indpath + with Induc -> + false + +let induct_discharge indpath statuslists cname destopt avoid (_,t) = + let (lstatus,rstatus) = statuslists in + let tophyp = ref None in + let (l,_) = decompose_prod t in + let n = List.length (filter (fun (_,t') -> is_rec_arg indpath t') l) in + let recvarname = + if n=1 then + cname + else if id_without_number cname then + id_of_string ((string_of_id cname)^"1") + else + id_of_string ((string_of_id cname)^"_1") + in + let hyprecname = id_of_string ("Hrec"^(string_of_id recvarname)) in + let rec peel_tac = function + | DOP2 (Prod,t,DLAM(na,DOP2(Prod,tr,DLAM(nar,c)))) + when is_rec_arg indpath t + -> if !tophyp=None then tophyp:=Some hyprecname;(* for lstatus *) + tclTHENLIST + [ central_intro (IBasedOn (recvarname,avoid)) destopt false; + central_intro (IBasedOn (hyprecname,avoid)) None false; + peel_tac c] + | DOP2 (Cast,c,t) -> peel_tac c + | DOP2 (Prod,t,DLAM(na,c)) + -> tclTHEN (central_intro (IAvoid avoid) destopt false) + (peel_tac c) + | _ -> tclIDTAC + in + let evaluated_peel_tac = peel_tac t in (* because side effect on tophyp *) + let newlstatus = (* if some Hrec has taken place at the top of hyps *) + List.map (function (hyp,None) -> (hyp,!tophyp) | x -> x) lstatus + in + tclTHENLIST [ evaluated_peel_tac; + intros_rmove rstatus; + intros_move newlstatus ] + +(* - le recalcul de indtyp à chaque itération de atomize_one est pour ne pas + s'embêter à regarder si un letin_tac ne fait pas des + substitutions aussi sur l'argument voisin *) + +(* Marche pas... faut prendre en compte l'occurence précise... *) + +let atomize_param_of_ind hyp0 gl = + let tmptyp0 = + try + (snd(lookup_sign hyp0 (pf_untyped_hyps gl))) + with Not_found -> + error ("No such hypothesis : " ^ (string_of_id hyp0)) + in + let (mind,indtyp,typ0) = pf_reduce_to_mind gl tmptyp0 in + let mis = mind_specif_of_mind mind in + let nparams = mis_nparams mis in + let argl = snd (decomp_app indtyp) in + let params = firstn nparams argl in + (* le gl est important pour ne pas préévaluer *) + let rec atomize_one i avoid gl = + if i<>nparams then + let tmptyp0 = pf_get_hyp gl hyp0 in + let (_,indtyp,_) = pf_reduce_to_mind gl tmptyp0 in + match (destAppL (whd_castapp indtyp)).(i) with + | VAR id when not (List.exists (occur_var id) avoid) -> + atomize_one (i-1) ((VAR id)::avoid) gl + | VAR id -> + let x = fresh_id [] id gl in + tclTHEN + (letin_tac true (Name x) (VAR id) (Some []) []) + (atomize_one (i-1) ((VAR x)::avoid)) gl + | c -> + let id = id_of_name_using_hdchar (pf_type_of gl c) Anonymous in + let x = fresh_id [] id gl in + tclTHEN + (letin_tac true (Name x) c (Some []) []) + (atomize_one (i-1) ((VAR x)::avoid)) gl + else + tclIDTAC gl + in + atomize_one (List.length argl) params gl + +let find_atomic_param_of_ind mind indtyp = + let mis = mind_specif_of_mind mind in + let nparams = mis_nparams mis in + let argl = snd (decomp_app indtyp) in + let argv = Array.of_list argl in + let params = firstn nparams argl in + let indvars = ref [] in + for i = nparams to (Array.length argv)-1 do + match argv.(i) with + | VAR id when not (List.exists (occur_var id) params) -> + indvars := add_set id !indvars + | _ -> () + done; + !indvars + + (* [cook_sign] builds the lists [indhyps] of hyps that must be + erased, the lists of hyps to be generalize [(hdeps,tdeps)] on the + goal together with the places [(lstatus,rstatus)] where to re-intro + them after induction. To know where to re-intro the dep hyp, we + remember the name of the hypothesis [lhyp] after which (if the dep + hyp is more recent than [hyp0]) or [rhyp] before which (if older + than [hyp0]) its equivalent must be moved when the induction has + been applied. Since computation of dependencies and [rhyp] is from + more ancient (on the right) to more recent hyp (on the left) but + the computation of [lhyp] progresses from the other way, [cook_hyp] + is in two passes (an alternative would have been to write an + higher-order algorithm). We strongly use references to reduce + the accumulation of arguments. + + To summarize, the situation looks like this + + Goal(n,x) -| H6:(Q n); x:A; H5:True; H4:(le O n); H3:(P n); H2:True; n:nat + Left Right + + Induction hypothesis is H4 ([hyp0]) + Variable parameters of (le O n) is the singleton list with "n" ([indvars]) + Part of [indvars] really in context is the same ([indhyps] + The dependent hyps are H3 and H6 ([dephyps]) + For H3 the memorized places are H5 ([lhyp]) and H2 ([rhyp]) + because these names are among the hyp which are fixed through the induction + For H6 the neighbours are None ([lhyp]) and H5 ([rhyp]) + For H3, because on the right of H4, we remember rhyp (here H2) + For H6, because on the left of H4, we remember lhyp (here None) + For H4, we remember lhyp (here H5) + + The right neighbour is then translated into the left neighbour + because move_hyp tactic needs the name of the hyp _after_ which we + move the hyp to move. + + But, say in the 2nd subgoal of the hypotheses, the goal will be + + (m:nat)((P m)->(Q m)->(Goal m)) -> (P Sm)-> (Q Sm)-> (Goal Sm) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ + both go where H4 was goes where goes where + H3 was H6 was + + We have to intro and move m and the recursive hyp first, but then + where to move H3 ??? Only the hyp on its right is relevant, but we + have to translate it into the name of the hyp on the left + + Note: this case where some hyp(s) in [dephyps] has(have) the same + left neighbour as [hyp0] is the only problematic case with right + neighbours. For the other cases (e.g. an hyp H1:(R n) between n and H2 + would have posed no problem. But for uniformity, we decided to use + the right hyp for all hyps on the right of H4. + + Others solutions are welcome *) + +exception Shunt of identifier option + +let cook_sign hyp0 indvars sign = + (* First pass from L to R: get [indhyps], [dephyps] and [statuslist] + for the hypotheses before (= more ancient than) hyp0 (see above) *) + let allindhyps = hyp0::indvars in + let indhyps = ref [] in + let hdeps = ref [] in + let tdeps = ref [] in + let ldeps = ref [] in + let rstatus = ref [] in + let lstatus = ref [] in + let before = ref true in + let seek_deps hyp typ rhyp = + if hyp = hyp0 then begin + before:=false; + None (* fake value *) + end else if List.mem hyp indvars then begin + indhyps := hyp::!indhyps; + rhyp + end else if (List.exists (fun id -> occur_var id typ) allindhyps + or List.exists (fun id -> occur_var id typ) !hdeps) then begin + hdeps := hyp::!hdeps; + tdeps := typ::!tdeps; + if !before then + rstatus := (hyp,rhyp)::!rstatus + else + ldeps := hyp::!ldeps; (* status calculé lors de la 2ème passe *) + Some hyp + end else + Some hyp + in + let _ = sign_it seek_deps sign None in + (* 2nd pass from R to L: get left hyp of [hyp0] and [lhyps] *) + let compute_lstatus lhyp hyp typ = + if hyp = hyp0 then raise (Shunt lhyp); + if List.mem hyp !ldeps then begin + lstatus := (hyp,lhyp)::!lstatus; + lhyp + end else + (Some hyp) + in + try + let _ = it_sign compute_lstatus None sign in anomaly "hyp0 not found" + with Shunt lhyp0 -> + let statuslists = (!lstatus,List.rev !rstatus) in + let deps = (List.rev !hdeps, List.rev !tdeps) in + (statuslists, lhyp0, !indhyps, deps) + + +(* Vieille version en une seule passe grace à l'ordre supérieur mais + trop difficile à comprendre + +let cook_sign hyp0 indvars sign = + let finaldeps = ref ([],[]) in + let indhyps = ref [] in + let hyp0succ = ref None in + let cook_init (hdeps,tdeps) rhyp before = + finaldeps := (List.rev hdeps, List.rev tdeps); + (None, []) in + let cook_hyp compute_rhyp hyp typ ((hdeps,tdeps) as deps) = + fun rhyp before -> + match () with + _ when (List.mem hyp indvars) + -> let result = compute_rhyp deps rhyp before in + indhyps := hyp::!indhyps; result + | _ when hyp = hyp0 + -> let (lhyp,statl) = compute_rhyp deps rhyp true in + hyp0succ := lhyp; (None (* fake value *),statl) + | _ when (List.exists (fun id -> occur_var id typ) (hyp0::indvars) + or List.exists (fun id -> occur_var id typ) hdeps) + -> let deps' = (hyp::hdeps, typ::tdeps) in + let (lhyp,statl) = compute_rhyp deps' rhyp before in + let hyp = if before then lhyp else rhyp in + (lhyp,(DEPENDENT (before,hyp,hyp))::statl) + | _ -> + let (_,statl) = compute_rhyp deps (Some hyp) before + in (Some hyp, statl) + in let (_,statuslist) = it_sign cook_hyp cook_init sign ([],[]) None false in + (statuslist, !hyp0succ, !indhyps, !finaldeps) +*) + +let induction_tac varname typ (elimc,elimt) gl = + let c = VAR varname in + let (wc,kONT) = startWalk gl in + let indclause = make_clenv_binding wc (c,typ) [] in + let elimclause = make_clenv_binding wc (DOP2(Cast,elimc,elimt),elimt) [] in + elimination_clause_scheme kONT wc elimclause indclause gl + +let get_constructors varname (elimc,elimt) mind mindpath = + (* Je suppose que w_type_of=type_of pour les constantes comme elimc *) + (* J'espere que je ne me trompe pas *) + let (hyps_of_elimt,_) = decompose_prod elimt in + let mis = mind_specif_of_mind mind in + let nconstr = mis_nconstr mis in + let nparam = mis_nparams mis in + try + List.rev (firstn nconstr (lastn (nconstr + nparam + 1) hyps_of_elimt)) + with Failure _ -> + anomaly "induct_elim: bad elimination predicate" + +let induction_from_context hyp0 gl = + (*test suivant sans doute inutile car protégé par le letin_tac avant appel*) + if List.mem hyp0 (ids_of_sign (initial_sign ())) then + errorlabstrm "induction" [< 'sTR "Cannot generalize a global variable" >]; + let sign = pf_untyped_hyps gl in + let tsign = pf_hyps gl in + let tmptyp0 = pf_get_hyp gl hyp0 in + let (mind,indtyp,typ0) = pf_reduce_to_mind gl tmptyp0 in + let indvars = find_atomic_param_of_ind mind indtyp in + let mindpath = mind_path mind in + let elimc = lookup_eliminator tsign mindpath (suff gl (pf_concl gl)) in + let elimt = pf_type_of gl elimc in + let (statlists,lhyp0,indhyps,deps) = cook_sign hyp0 indvars sign in + let (dephyps,deptyps) = deps in + let tmpcl = List.fold_right2 mkNamedProd dephyps deptyps (pf_concl gl) in + let lc = get_constructors hyp0 (elimc,elimt) mind mindpath in + tclTHENLIST + [ apply_type tmpcl (List.map (fun id -> VAR id) dephyps); + thin dephyps; + tclTHENS + (tclTHEN + (induction_tac hyp0 typ0 (elimc,elimt)) + (thin (hyp0::(List.rev indhyps)))) + (List.map (induct_discharge mindpath statlists hyp0 lhyp0 dephyps) lc)] + gl + +let induction_with_atomization_of_ind_arg hyp0 = + tclTHEN + (atomize_param_of_ind hyp0) + (induction_from_context hyp0) + +let new_induct c gl = + match c with + | (VAR id) when not (List.mem id (ids_of_sign (initial_sign ()))) -> + tclORELSE + (tclTHEN (intros_until id) (tclLAST_HYP simplest_elim)) + (induction_with_atomization_of_ind_arg id) gl + | _ -> + let x = id_of_name_using_hdchar (pf_type_of gl c) Anonymous in + let id = fresh_id [] x gl in + tclTHEN + (letin_tac true (Name id) c (Some []) []) + (induction_with_atomization_of_ind_arg id) gl + +(***) + +(* The registered tactic, which calls the default elimination + * if no elimination constant is provided. *) + +let dyn_elim = function + | [Constr mp; Cbindings mpbinds] -> + default_elim (mp,mpbinds) + | [Command mp; Bindings mpbinds] -> + tactic_com_bind_list default_elim (mp,mpbinds) + | [Command mp; Bindings mpbinds; Command elimc; Bindings elimcbinds] -> + let funpair2funlist f = (function [x;y] -> f x y | _ -> assert false) in + tactic_com_bind_list_list + (funpair2funlist general_elim) + [(mp,mpbinds);(elimc,elimcbinds)] + | [Constr mp; Cbindings mpbinds; Constr elimc; Cbindings elimcbinds] -> + general_elim (mp,mpbinds) (elimc,elimcbinds) + | l -> bad_tactic_args "elim" l + +(* Induction tactics *) + +let induct s = + tclORELSE (tclTHEN (intros_until s) (tclLAST_HYP simplest_elim)) + (induction_from_context s) + +let induct_nodep n = tclTHEN (intros_do n) (tclLAST_HYP simplest_elim) + +(* Pour le futur +let dyn_induct = function + | [(COMMAND c)] -> tactic_com new_induct x + | [(CONSTR x)] -> new_induct x + | [(INTEGER n)] -> induct_nodep n + | l -> bad_tactic_args "induct" l +*) + +let dyn_induct = function + | [Identifier x] -> induct x + | [Integer n] -> induct_nodep n + | l -> bad_tactic_args "induct" l + +(* Case analysis tactics *) + +let general_case_analysis (c,lbindc) gl = + let (mind,_,_) = reduce_to_mind (project gl) (pf_type_of gl c) in + let sigma = project gl in + let sort = sort_of_goal gl in + let elim = Indrec.make_case_gen sigma mind sort in + general_elim (c,lbindc) (elim,[]) gl + +let simplest_case c = general_case_analysis (c,[]) + +let dyn_case =function + | [(CONSTR mp);(CBINDINGS mpbinds)] -> + general_case_analysis (mp,mpbinds) + | [(COMMAND mp);(BINDINGS mpbinds)] -> + tactic_com_bind_list general_case_analysis (mp,mpbinds) + | l -> bad_tactic_args "case" l + + +(* Destruction tactics *) + +let destruct s = (tclTHEN (intros_until s) (tclLAST_HYP simplest_case)) +let destruct_nodep n = (tclTHEN (intros_do n) (tclLAST_HYP simplest_case)) + +let dyn_destruct = function + | [(IDENTIFIER x)] -> destruct x + | [(INTEGER n)] -> destruct_nodep n + | l -> bad_tactic_args "destruct" l + +(* + * Eliminations giving the type instead of the proof. + * These tactics use the default elimination constant and + * no substitutions at all. + * May be they should be integrated into Elim ... + *) + +let elim_scheme_type elim t gl = + let (wc,kONT) = startWalk gl in + let clause = mk_clenv_type_of wc elim in + match last_arg (clenv_template clause).rebus with + | DOP0(Meta mv) -> + let clause' = clenv_unify (clenv_instance_type clause mv) t clause in + elim_res_pf kONT clause' gl + | _ -> anomaly "elim_scheme_type" + +let elim_type t gl = + let (path_name,tind,t) = reduce_to_ind (project gl) t in + let elimc = + lookup_eliminator (pf_hyps gl) path_name (suff gl (pf_concl gl)) + in + match t with + | DOP2(Prod,_,_) -> error "Not an inductive definition" + | _ -> elim_scheme_type elimc tind gl + +let dyn_elim_type = function + | [(CONSTR c)] -> elim_type c + | [(COMMAND com)] -> tactic_com_sort elim_type com + | l -> bad_tactic_args "elim_type" l + +let case_type t gl = + let (mind,_,t) = reduce_to_mind (project gl) t in + match t with + | DOP2(Prod,_,_) -> error "Not an inductive definition" + | _ -> + let sigma = project gl in + let sort = sort_of_goal gl in + let elimc = Indrec.make_case_gen sigma mind sort in + elim_scheme_type elimc t gl + +let dyn_case_type = function + | [CONSTR c] -> case_type c + | [COMMAND com] -> tactic_com case_type com + |l -> bad_tactic_args "case_type" l + + +(* Some eliminations frequently used *) + +(* These elimination tactics are particularly adapted for sequent + calculus. They take a clause as argument, and yield the + elimination rule if the clause is of the form (Some id) and a + suitable introduction rule otherwise. They do not depend on + the name of the eliminated constant, so they can be also + used on ad-hoc disjunctions and conjunctions introduced by + the user. + -- Eduardo Gimenez (11/8/97) + + HH (29/5/99) replaces failures by specific error messages + *) + +let andE id gl = + let t = pf_get_hyp gl id in + if is_conjunction (pf_hnf_constr gl t) then + (tclTHEN (simplest_elim (VAR id)) (tclDO 2 intro)) gl + else + errorlabstrm "andE" + [< 'sTR("Tactic andE expects "^(string_of_id id)^" is a conjunction.")>] + +let dAnd cls gl = + match cls with + | None -> simplest_split gl + | Some id -> andE id gl + +let orE id gl = + let t = pf_get_hyp gl id in + if is_disjunction (pf_hnf_constr gl t) then + (tclTHEN (simplest_elim (VAR id)) intro) gl + else + errorlabstrm "orE" + [< 'sTR("Tactic orE expects "^(string_of_id id)^" is a disjunction.")>] + +let dorE b cls gl = + match cls with + | (Some id) -> orE id gl + | None -> (if b then right else left) [] gl + +let impE id gl = + let t = pf_get_hyp gl id in + if is_imp_term (pf_hnf_constr gl t) then + let (dom, _, rng) = destProd (pf_hnf_constr gl t) in + (tclTHENS (cut_intro rng) + [tclIDTAC;apply_term (VAR id) [DOP0(Meta(newMETA()))]]) gl + else + errorlabstrm "impE" + [< 'sTR("Tactic impE expects "^(string_of_id id)^ + " is a an implication.")>] + +let dImp cls gl = + match cls with + | None -> intro gl + | Some id -> impE id gl + +(******************************************) +(* Instantiation of existential variables *) +(******************************************) + +let instantiate_pf n c pfts = + let gls = top_goal_of_pftreestate pfts in + let (wc,_) = startWalk gls in + let sigma = (w_Underlying wc) in + let (sp,_) = + try + List.nth (Evd.non_instantiated sigma) (n-1) + with Failure _ -> + error "not so many uninstantiated existential variables" + in + let wc' = w_Define sp c wc in + let newgc = ts_mk (w_Underlying wc') in + change_constraints_pftreestate newgc pfts + +let instantiate_pf_com n com pfts = + let gls = top_goal_of_pftreestate pfts in + let (wc,_) = startWalk gls in + let sigma = (w_Underlying wc) in + let (sp,evd) = + try + List.nth (Evd.non_instantiated sigma) (n-1) + with Failure _ -> + error "not so many uninstantiated existential variables" + in + let c = constr_of_com sigma evd.hyps com in + let wc' = w_Define sp c wc in + let newgc = ts_mk (w_Underlying wc') in + change_constraints_pftreestate newgc pfts + +(************************************************) +(* Tactics related with logic connectives *) +(************************************************) + +(* Contradiction *) + +let contradiction_on_hyp id gl = + let hyp = pf_get_hyp gl id in + if is_empty_type hyp then + simplest_elim (VAR id) gl + else + error "Not a contradiction" + +(* Absurd *) +let absurd c gls = + let falseterm = pf_constr_of_com_sort gls (nvar "False") in + (tclTHENS + (tclTHEN (elim_type falseterm) (cut c)) + ([(tclTHENS + (cut (applist(pf_global gls (id_of_string "not"),[c]))) + ([(tclTHEN (intros) + ((fun gl -> + let (ida,_) = pf_nth_hyp gl 1 + and (idna,_) = pf_nth_hyp gl 2 in + exact (applist(VAR idna,[VAR ida])) gl))); + tclIDTAC])); + tclIDTAC])) gls + +let dyn_absurd = function + | [(CONSTR c)] -> absurd c + | [(COMMAND com)] -> tactic_com_sort absurd com + | l -> bad_tactic_args "absurd" l + +let contradiction gls = + let falseterm = pf_constr_of_com_sort gls (nvar "False") in + tclTHENLIST [ intros; elim_type falseterm; assumption ] gls + +let dyn_contradiction = function + | [] -> contradiction + | l -> bad_tactic_args "contradiction" l + +(* Relfexivity tactics *) + +let reflexivity gl = + match match_with_equation (pf_concl gl) with + | None -> error "The conclusion is not a substitutive equation" + | Some (hdcncl,args) -> one_constructor 1 [] gl + +let intros_reflexivity = (tclTHEN intros reflexivity) + +let dyn_reflexivity = function + | [] -> intros_reflexivity + | _ -> errorlabstrm "Tactics.reflexivity" + [<'sTR "Tactic applied to bad arguments!">] + +(* Symmetry tactics *) + +(* This tactic first tries to apply a constant named sym_eq, where eq + is the name of the equality predicate. If this constant is not + defined and the conclusion is a=b, it solves the goal doing (Cut + b=a;Intro H;Case H;Constructor 1) *) + +let symmetry gl = + match match_with_equation (pf_concl gl) with + | None -> error "The conclusion is not a substitutive equation" + | Some (hdcncl,args) -> + let hdcncls = string_head hdcncl in + begin + try + (apply (pf_parse_const gl ("sym_"^hdcncls)) gl) + with _ -> + let symc = match args with + | [typ;c1;c2] -> mkAppL [| hdcncl; typ; c2; c1 |] + | [c1;c2] -> mkAppL [| hdcncl; c2; c1 |] + | _ -> assert false + in + (tclTHENS (cut symc) + [ tclTHENLIST [ intro; + tclLAST_HYP simplest_case; + one_constructor 1 [] ]; + tclIDTAC ]) gl + end + +let intros_symmetry = (tclTHEN intros symmetry) + +let dyn_symmetry = function + | [] -> intros_symmetry + | l -> bad_tactic_args "symmetry" l + +(* Transitivity tactics *) + +(* This tactic first tries to apply a constant named trans_eq, where eq + is the name of the equality predicate. If this constant is not + defined and the conclusion is a=b, it solves the goal doing + Cut x1=x2; + [Cut x2=x3; [Intros e1 e2; Case e2;Assumption + | Idtac] + | Idtac] + --Eduardo (19/8/97) +*) + +let transitivity t gl = + match match_with_equation (pf_concl gl) with + | None -> error "The conlcusion is not a substitutive equation" + | Some (hdcncl,args) -> + let hdcncls = string_head hdcncl in + begin + try + apply_list [(pf_parse_const gl ("trans_"^hdcncls));t] gl + with _ -> + let eq1 = match args with + | [typ;c1;c2] -> mkAppL [| hdcncl; typ; c1; t |] + | [c1;c2] -> mkAppL [| hdcncl; c1; t|] + | _ -> assert false + in + let eq2 = match args with + | [typ;c1;c2] -> mkAppL [| hdcncl; typ; t; c2 |] + | [c1;c2] -> mkAppL [| hdcncl; t; c2 |] + | _ -> assert false + in + (tclTHENS (cut eq2) + [tclTHENS (cut eq1) + [ tclTHENLIST [ tclDO 2 intro; + tclLAST_HYP simplest_case; + assumption ]; + tclIDTAC]; + tclIDTAC])gl + end + +let intros_transitivity n = tclTHEN intros (transitivity n) + +let dyn_transitivity = function + | [(CONSTR n)] -> intros_transitivity n + | [(COMMAND n)] -> tactic_com intros_transitivity n + | l -> bad_tactic_args "transitivity" l + +(* tactical to save as name a subproof such that the generalisation of + the current goal, abstracted with respect to the local signature, + is solved by tac *) + +let abstract_subproof name tac gls = + let current_sign = initial_sign() + and global_sign = pf_untyped_hyps gls in + let sign = Names.sign_it + (fun id typ s -> + if mem_sign current_sign id then s else add_sign (id,typ) s) + global_sign nil_sign + in + let na = next_global_ident_away name + (ids_of_sign global_sign) in + let nas = string_of_id na in + let concl = Names.it_sign (fun t id typ -> mkNamedProd id typ t) + (pf_concl gls) sign in + let top_goal = mkGOAL (mt_ctxt Spset.empty) current_sign concl in + let ts = {top_hyps = initial_assumptions(); + top_goal = top_goal; + top_strength = Constrtypes.NeverDischarge} + in + start(nas,ts);set_proof (Some nas); + begin + try + by (tclCOMPLETE (tclTHEN (tclDO (sign_length sign) intro) + tac)); + save_named true + with UserError _ as e -> + (abort_cur_goal(); raise e) + end; + exact (applist ((Machops.global (gLOB current_sign) na), + (List.map (fun id -> VAR(id)) + (List.rev (ids_of_sign sign))))) + gls + +let tclABSTRACT name_op tac gls = + let s = match name_op with + | Some s -> s + | None -> id_of_string ((get_proof ())^"_subproof") + in + abstract_subproof s tac gls + +let dyn_tclABSTRACT = + hide_tactic "ABSTRACT" + (function + | [TACEXP tac] -> + tclABSTRACT None (Tacinterp.interp tac) + | [IDENTIFIER s; TACEXP tac] -> + tclABSTRACT (Some s) (Tacinterp.interp tac) + | _ -> invalid_arg "tclABSTRACT") diff --git a/tactics/tactics.mli b/tactics/tactics.mli new file mode 100644 index 000000000..db60df966 --- /dev/null +++ b/tactics/tactics.mli @@ -0,0 +1,244 @@ + +(* $Id$ *) + +(*i*) +open Names +open Term +open Tacmach +open Proof_trees +open Reduction +open Evd +open Clenv +open Tacred +open Tacticals +(*i*) + +(* Main tactics. *) + +(*s General functions. *) + +val make_clenv_binding_apply : + walking_constraints -> constr * constr -> constr substitution -> + walking_constraints clausenv + +val type_clenv_binding : walking_constraints -> + constr * constr -> constr substitution -> constr + +val string_head : constr -> string +val head_constr : constr -> constr list +val head_constr_bound : constr -> constr list -> constr list +val bad_tactic_args : string -> tactic_arg list -> 'a + +exception Bound + +(*s Primitive tactics. *) + +val introduction : identifier -> tactic +val refine : constr -> tactic +val convert_concl : constr -> tactic +val convert_hyp : identifier -> constr -> tactic +val thin : identifier list -> tactic +val mutual_fix : identifier list -> int list -> constr list -> tactic +val fix : identifier -> int -> tactic +val mutual_cofix : identifier list -> constr list -> tactic +val cofix : identifier -> tactic + +val dyn_mutual_fix : tactic_arg list -> tactic +val dyn_mutual_cofix : tactic_arg list -> tactic + +(*s Introduction tactics. *) + +val next_global_ident_away : identifier -> identifier list -> identifier + +val intro : tactic +val introf : tactic +val intro_force : bool -> tactic +val dyn_intro : tactic_arg list -> tactic + +val dyn_intro_move : tactic_arg list -> tactic + +val intro_replacing : identifier -> tactic +val intro_using : identifier -> tactic +val intro_using_warning : identifier -> tactic +val intros_using : identifier list -> tactic +val intro_erasing : identifier -> tactic + +val intros : tactic + +(*i Obsolete, subsumed by Elim.dyn_intro_pattern +val dyn_intros_using : tactic_arg list -> tactic +i*) + +val intros_until : identifier -> tactic +val dyn_intros_until : tactic_arg list -> tactic + +val intros_clearing : bool list -> tactic + +(*s Exact tactics. *) + +val assumption : tactic +val dyn_assumption : tactic_arg list -> tactic + +val exact : constr -> tactic +val dyn_exact : tactic_arg list -> tactic + +(*s Reduction tactics. *) + +val reduct_in_hyp : 'a reduction_function -> identifier -> tactic +val reduct_option : 'a reduction_function -> identifier option -> tactic +val reduct_in_concl : 'a reduction_function -> tactic +val change_in_concl : constr -> tactic + +val change_in_hyp : constr -> identifier -> tactic +val change_option : constr -> identifier option -> tactic +val red_in_concl : tactic +val red_in_hyp : identifier -> tactic +val red_option : identifier option -> tactic +val hnf_in_concl : tactic +val hnf_in_hyp : identifier -> tactic +val hnf_option : identifier option -> tactic +val simpl_in_concl : tactic +val simpl_in_hyp : identifier -> tactic +val simpl_option : identifier option -> tactic +val normalise_in_concl: tactic +val normalise_in_hyp : identifier -> tactic +val normalise_option : identifier option -> tactic +val unfold_in_concl : (int list * section_path) list -> tactic +val unfold_in_hyp : + (int list * section_path) list -> identifier -> tactic +val unfold_option : + (int list * section_path) list -> identifier option -> tactic +val reduce : red_expr -> identifier list -> tactic +val dyn_reduce : tactic_arg list -> tactic +val dyn_change : tactic_arg list -> tactic + +val unfold_constr : constr -> tactic +val pattern_option : + (int list * constr * constr) list -> identifier option -> tactic + +(*s Modification of the local context. *) + +val clear : identifier list -> tactic +val clear_one : identifier -> tactic +val dyn_clear : tactic_arg list -> tactic + +val clear_clauses : clause list -> tactic +val clear_clause : clause -> tactic + +val new_hyp : int option ->constr -> constr substitution -> tactic +val dyn_new_hyp : tactic_arg list -> tactic + +val dyn_move : tactic_arg list -> tactic +val dyn_move_dep : tactic_arg list -> tactic + +(*s Resolution tactics. *) + +val apply_type : constr -> constr list -> tactic +val apply_term : constr -> constr list -> tactic +val bring_hyps : clause list -> tactic + +val apply : constr -> tactic +val apply_without_reduce : constr -> tactic +val apply_list : constr list -> tactic +val apply_with_bindings : (constr * constr substitution) -> tactic +val dyn_apply : tactic_arg list -> tactic + +val cut_and_apply : constr -> tactic +val dyn_cut_and_apply : tactic_arg list -> tactic + +(*s Instantiation tactics. *) + +val instantiate_pf : int -> constr -> pftreestate -> pftreestate + +(*s Elimination tactics. *) + +val general_elim : constr * constr substitution -> + constr * constr substitution -> tactic +val default_elim : constr * constr substitution -> tactic +val simplest_elim : constr -> tactic +val dyn_elim : tactic_arg list -> tactic + +val induct : identifier -> tactic +val induct_nodep : int -> tactic +val dyn_induct : tactic_arg list -> tactic + +(*s Case analysis tactics. *) + +val general_case_analysis : constr * constr substitution -> tactic +val simplest_case : constr -> tactic +val dyn_case : tactic_arg list -> tactic + +val destruct : identifier -> tactic +val destruct_nodep : int -> tactic +val dyn_destruct : tactic_arg list -> tactic + +(*s Eliminations giving the type instead of the proof. *) + +val case_type : constr -> tactic +val dyn_case_type : tactic_arg list -> tactic + +val elim_type : constr -> tactic +val dyn_elim_type : tactic_arg list -> tactic + + +(*s Some eliminations which are frequently used. *) + +val impE : identifier -> tactic +val andE : identifier -> tactic +val orE : identifier -> tactic +val dImp : clause -> tactic +val dAnd : clause -> tactic +val dorE : bool -> clause ->tactic + + +(*s Introduction tactics. *) + +val constructor_checking_bound : int option -> int -> + constr substitution -> tactic +val one_constructor : int -> constr substitution -> tactic +val any_constructor : tactic +val left : constr substitution -> tactic +val simplest_left : tactic +val right : constr substitution -> tactic +val simplest_right : tactic +val split : constr substitution -> tactic +val simplest_split : tactic + +val dyn_constructor : tactic_arg list -> tactic +val dyn_left : tactic_arg list -> tactic +val dyn_right : tactic_arg list -> tactic +val dyn_split : tactic_arg list -> tactic + +(*s Logical connective tactics. *) + +val absurd : constr -> tactic +val dyn_absurd : tactic_arg list -> tactic + +val contradiction_on_hyp : identifier -> tactic +val contradiction : tactic +val dyn_contradiction : tactic_arg list -> tactic + +val reflexivity : tactic +val intros_reflexivity : tactic +val dyn_reflexivity : tactic_arg list -> tactic + +val symmetry : tactic +val intros_symmetry : tactic +val dyn_symmetry : tactic_arg list -> tactic + +val transitivity : constr -> tactic +val intros_transitivity : constr -> tactic +val dyn_transitivity : tactic_arg list -> tactic + +val cut : constr -> tactic +val cut_intro : constr -> tactic +val cut_replacing : identifier -> constr -> tactic +val cut_in_parallel : constr list -> tactic +val dyn_cut : tactic_arg list -> tactic +val dyn_lettac : tactic_arg list -> tactic + +val generalize : constr list -> tactic +val dyn_generalize : tactic_arg list -> tactic +val dyn_generalize_dep : tactic_arg list -> tactic + +val tclABSTRACT : identifier option -> tactic -> tactic |