summaryrefslogtreecommitdiff
path: root/backends/hol4/primitivesBaseTacLib.sml
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--backends/hol4/primitivesBaseTacLib.sml399
1 files changed, 397 insertions, 2 deletions
diff --git a/backends/hol4/primitivesBaseTacLib.sml b/backends/hol4/primitivesBaseTacLib.sml
index b5d9918e..0368ee9a 100644
--- a/backends/hol4/primitivesBaseTacLib.sml
+++ b/backends/hol4/primitivesBaseTacLib.sml
@@ -13,6 +13,8 @@ fun ignore_tac (_ : thm) : tactic = ALL_TAC
val pop_ignore_tac = pop_assum ignore_tac
+val try_tac = TRY
+
(* TODO: no exfalso tactic?? *)
val ex_falso : tactic =
SUBGOAL_THEN “F” (fn th => ASSUME_TAC th >> fs[])
@@ -22,13 +24,406 @@ fun qspecl_assume xl th = qspecl_then xl assume_tac th
fun first_qspec_assume x = first_assum (qspec_assume x)
fun first_qspecl_assume xl = first_assum (qspecl_assume xl)
-
-
+val all_tac = all_tac
val cooper_tac = COOPER_TAC
+val pure_rewrite_tac = PURE_REWRITE_TAC
val pure_once_rewrite_tac = PURE_ONCE_REWRITE_TAC
(* Dependent rewrites *)
val dep_pure_once_rewrite_tac = dep_rewrite.DEP_PURE_ONCE_REWRITE_TAC
val dep_pure_rewrite_tac = dep_rewrite.DEP_PURE_REWRITE_TAC
+
+(* Add a list of theorems in the assumptions *)
+fun assume_tacl (thms : thm list) : tactic =
+ let
+ (* TODO: use MAP_EVERY *)
+ fun t thms =
+ case thms of
+ [] => all_tac
+ | thm :: thms => assume_tac thm >> t thms
+ in
+ t thms
+ end
+
+(* Given a theorem of the shape:
+ {[
+ A0, ..., An ⊢ B0 ==> ... ==> Bm ==> concl
+ ]}
+
+ Rewrite it so that it has the shape:
+ {[
+ ⊢ (A0 /\ ... /\ An /\ B0 /\ ... /\ Bm) ==> concl
+ ]}
+ *)
+fun thm_to_conj_implies (thm : thm) : thm =
+ let
+ (* Discharge all the assumptions *)
+ val thm = DISCH_ALL thm;
+ (* Rewrite the implications as one conjunction *)
+ val thm = PURE_REWRITE_RULE [GSYM satTheory.AND_IMP] thm;
+ in thm end
+
+(* Like THEN1 and >-, but doesn't fail if the first subgoal is not completely
+ solved.
+
+ TODO: how to get the notation right?
+ *)
+fun op partial_then1 (tac1: tactic) (tac2: tactic) : tactic = tac1 THEN_LT (NTH_GOAL tac2 1)
+
+(* If the current goal is [asms ⊢ g], and given a lemma of the form
+ [⊢ H ==> C], do the following:
+ - introduce [asms ⊢ H] as a subgoal and apply the given tactic on it
+ - also calls the theorem tactic with the theorem [asms ⊢ C]
+
+ If the lemma is not an implication, we directly call the theorem tactic.
+ *)
+fun sg_premise_then (premise_tac: tactic) (then_tac: thm_tactic) (thm : thm) : tactic =
+ let
+ val c = concl thm;
+ (* First case: there is a premise to prove *)
+ fun prove_premise_then (h : term) =
+ partial_then1 (SUBGOAL_THEN h (fn h_thm => then_tac (MP thm h_thm))) premise_tac;
+ (* Second case: no premise to prove *)
+ val no_prove_premise_then = then_tac thm;
+ in
+ if is_imp c then prove_premise_then (fst (dest_imp c)) else no_prove_premise_then
+ end
+
+(* Add the first premise of the theorem as subgoal, and add the theorem without its
+ first premise as an assumption.
+
+ For instance, if we have the goal:
+ asls
+ ====
+ c
+ and the theorem: ⊢ H ==> G
+
+ We generate:
+ asls asls
+ ==== G
+ H ====
+ C
+
+*)
+val sg_premise_tac = sg_premise_then all_tac assume_tac
+
+(* Same as {!sg_premise_then} but fails if the premise_tac fails to prove the premise *)
+fun prove_premise_then (premise_tac: tactic) (then_tac: thm_tactic) (thm : thm) : tactic =
+ sg_premise_then
+ (premise_tac >> FAIL_TAC "prove_premise_then: could not prove premise")
+ then_tac thm
+
+(*
+val thm = th
+*)
+
+(* Call a function on all the subterms of a term *)
+fun dep_apply_in_subterms
+ (f : string Redblackset.set -> term -> unit)
+ (bound_vars : string Redblackset.set)
+ (t : term) : unit =
+ let
+ val dep = dep_apply_in_subterms f;
+ val _ = f bound_vars t;
+ in
+ case dest_term t of
+ VAR (name, ty) => ()
+ | CONST {Name=name, Thy=thy, Ty=ty} => ()
+ | COMB (app, arg) =>
+ let
+ val _ = dep bound_vars app;
+ val _ = dep bound_vars arg;
+ in () end
+ | LAMB (bvar, body) =>
+ let
+ val (varname, ty) = dest_var bvar;
+ val bound_vars = Redblackset.add (bound_vars, varname);
+ val _ = dep bound_vars body;
+ in () end
+ end
+
+(* Return the set of free variables appearing in the residues of a term substitution *)
+fun free_vars_in_subst_residue (s: (term, term) Term.subst) : string Redblackset.set =
+ let
+ val free_vars = free_varsl (map (fn {redex=_, residue=x} => x) s);
+ val free_vars = map (fst o dest_var) free_vars;
+ val free_vars = Redblackset.fromList String.compare free_vars;
+ in free_vars end
+
+(* Attempt to instantiate a rewrite theorem.
+
+ Remark: this theorem should be of the form:
+ H ⊢ x = y
+
+ (without quantified variables).
+
+ **REMARK**: the function raises a HOL_ERR exception if it fails.
+
+ [forbid_vars]: forbid substituting with those vars (typically because
+ we are maching in a subterm under lambdas, and some of those variables
+ are bounds in the outer lambdas).
+*)
+fun inst_match_concl (forbid_vars : string Redblackset.set) (th : thm) (t : term) : thm =
+ let
+ (* Retrieve the lhs of the conclusion of the theorem *)
+ val l = lhs (concl th);
+ (* Match this lhs with the term *)
+ val (var_s, ty_s) = match_term l t;
+ (* Check that we are allowed to perform the substitution *)
+ val free_vars = free_vars_in_subst_residue var_s;
+ val _ = assert Redblackset.isEmpty (Redblackset.intersection (free_vars, forbid_vars));
+ in
+ (* Perform the substitution *)
+ INST var_s (INST_TYPE ty_s th)
+ end
+
+(*
+val forbid_vars = Redblackset.empty String.compare
+val t = “u32_to_int (int_to_u32 x)”
+val t = “u32_to_int (int_to_u32 3)”
+val th = (UNDISCH_ALL o SPEC_ALL) int_to_u32_id
+*)
+
+(* Attempt to instantiate a theorem by matching its first premise.
+
+ Note that we make the hypothesis tha all the free variables which need
+ to be instantiated appear in the first premise, of course (the caller should
+ enforce this).
+
+ Remark: this theorem should be of the form:
+ ⊢ H0 ==> ... ==> Hn ==> H
+
+ (without quantified variables).
+
+ **REMARK**: the function raises a HOL_ERR exception if it fails.
+
+ [forbid_vars]: see [inst_match_concl]
+*)
+fun inst_match_first_premise
+ (forbid_vars : string Redblackset.set) (th : thm) (t : term) : thm =
+ let
+ (* Retrieve the first premise *)
+ val l = (fst o dest_imp o concl) th;
+ (* Match this with the term *)
+ val (var_s, ty_s) = match_term l t;
+ (* Check that we are allowed to perform the substitution *)
+ val free_vars = free_vars_in_subst_residue var_s;
+ val _ = assert Redblackset.isEmpty (Redblackset.intersection (free_vars, forbid_vars));
+ in
+ (* Perform the substitution *)
+ INST var_s (INST_TYPE ty_s th)
+ end
+
+(*
+val forbid_vars = Redblackset.empty String.compare
+val t = “u32_to_int z = u32_to_int i − 1”
+val th = SPEC_ALL NUM_SUB_1_EQ
+*)
+
+(* Call a matching function on all the subterms in the provided list of term.
+ This is a generic function.
+
+ [try_match] should return an instantiated theorem, as well as a term which
+ identifies this theorem (the lhs of the equality, if this is a rewriting
+ theorem for instance - we use this to check for collisions, and discard
+ redundant instantiations).
+ *)
+fun inst_match_in_terms
+ (try_match: string Redblackset.set -> term -> term * thm)
+ (tl : term list) : thm list =
+ let
+ (* We use a map when storing the theorems, to avoid storing the same theorem twice *)
+ val inst_thms: (term, thm) Redblackmap.dict ref = ref (Redblackmap.mkDict Term.compare);
+ fun try_instantiate bvars t =
+ let
+ val (inst_th_tm, inst_th) = try_match bvars t;
+ in
+ inst_thms := Redblackmap.insert (!inst_thms, inst_th_tm, inst_th)
+ end
+ handle HOL_ERR _ => ();
+ (* Explore the term *)
+ val _ = app (dep_apply_in_subterms try_instantiate (Redblackset.empty String.compare)) tl;
+ in
+ map snd (Redblackmap.listItems (!inst_thms))
+ end
+
+(* Given a rewriting theorem [th] which has premises, return all the
+ instantiations of this theorem which make its conclusion match subterms
+ in the provided list of term.
+
+ [ignore]: if the conclusion of a theorem matches a term in this set, ignore it.
+ *)
+fun inst_match_concl_in_terms (ignore : term Redblackset.set) (th : thm) (tl : term list) : thm list =
+ let
+ val th = (UNDISCH_ALL o SPEC_ALL) th;
+ fun try_match bvars t =
+ let
+ val inst_th = inst_match_concl bvars th t;
+ val c = concl inst_th;
+ in
+ (* Check that we mustn't ignore the theorem *)
+ if Redblackset.member (ignore, c) then failwith "inst_match_concl_in_terms: already in the assumptions"
+ else (lhs (concl inst_th), inst_th)
+ end;
+ in
+ inst_match_in_terms try_match tl
+ end
+
+(*
+val t = “!x. u32_to_int (int_to_u32 x) = u32_to_int (int_to_u32 y)”
+val th = int_to_u32_id
+
+val thms = inst_match_concl_in_terms int_to_u32_id [t]
+*)
+
+
+(* Given a theorem [th] which has premises, return all the
+ instantiations of this theorem which make its first premise match subterms
+ in the provided list of term.
+ *)
+fun inst_match_first_premise_in_terms (th : thm) (tl : term list) : thm list =
+ let
+ val th = SPEC_ALL th;
+ fun try_match bvars t =
+ let
+ val inst_th = inst_match_first_premise bvars th t;
+ in
+ ((fst o dest_imp o concl) inst_th, inst_th)
+ end;
+ in
+ inst_match_in_terms try_match tl
+ end
+
+(*
+val t = “x = y - 1 ==> T”
+val th = SPEC_ALL NUM_SUB_1_EQ
+
+val thms = inst_match_first_premise_in_terms th [t]
+*)
+
+
+(* Attempt to apply dependent rewrites with a theorem by matching its
+ conclusion with subterms in the given list of terms.
+
+ Leaves the premises as subgoals if [prove_premise] doesn't prove them.
+ *)
+fun apply_dep_rewrites_match_concl_with_terms_tac
+ (prove_premise : tactic) (then_tac : thm_tactic) (tl : term list) (th : thm) : tactic =
+ let
+ val ignore = Redblackset.fromList Term.compare tl;
+ (* Discharge the assumptions so that the goal is one single term *)
+ val thms = inst_match_concl_in_terms ignore th tl;
+ val thms = map thm_to_conj_implies thms;
+ in
+ (* Apply each theorem *)
+ MAP_EVERY (sg_premise_then prove_premise then_tac) thms
+ end
+
+(* Attempt to apply dependent rewrites with a theorem by matching its
+ conclusion with subterms of the goal (including the assumptions).
+
+ Leaves the premises as subgoals if [prove_premise] doesn't prove them.
+ *)
+fun apply_dep_rewrites_match_concl_with_all_tac
+ (prove_premise : tactic) (then_tac : thm_tactic) (th : thm) : tactic =
+ fn (asms, g) => apply_dep_rewrites_match_concl_with_terms_tac prove_premise then_tac (g :: asms) th (asms, g)
+
+(* Same as {!apply_dep_rewrites_match_concl_with_all_tac} but we only match the
+ conclusion of the goal.
+ *)
+fun apply_dep_rewrites_match_concl_with_goal_tac
+ (prove_premise : tactic) (then_tac : thm_tactic) (th : thm) : tactic =
+ fn (asms, g) => apply_dep_rewrites_match_concl_with_terms_tac prove_premise then_tac [g] th (asms, g)
+
+(* A theorem might be of the shape: [H => A = B /\ C = D], in which
+ case we can split it into:
+ [H => A = B]
+ [H => C = D]
+
+ The theorem mustn't have assumptions.
+ *)
+fun split_rewrite_thm (th : thm) : thm list =
+ let
+ val _ = null (hyp th);
+ val t = concl th;
+ val (vars, t) = strip_forall t;
+ val (impl, t) = strip_imp t;
+ val tl = strip_conj t;
+ fun mk_goal (t : term) = list_mk_forall (vars, (list_mk_imp (impl, t)))
+ val prove_tac =
+ rpt gen_tac >> rpt disch_tac >>
+ qspecl_assume (map (fn a => ‘^a’) vars) th >>
+ fs [];
+ fun mk_th (t : term) = prove (mk_goal t, prove_tac);
+ (* Change the shape of the theorem (one of the conjuncts may have
+ universally quantified variables)
+ *)
+ fun transform_th (th : thm) : thm =
+ (GEN_ALL o thm_to_conj_implies o SPEC_ALL o UNDISCH_ALL o SPEC_ALL) th
+ in
+ map (transform_th o mk_th) tl
+ end
+
+(* A dependent rewrite tactic which introduces the premises in a new goal.
+
+ We try to apply dependent rewrites to the whole goal, including its assumptions.
+
+ TODO: this tactic sometimes introduces several times the same subgoal
+ (because we split theorems...).
+ *)
+fun sg_dep_rewrite_all_tac (th : thm) =
+ let
+ (* Split the theorem *)
+ val thml = split_rewrite_thm th
+ in
+ MAP_EVERY (apply_dep_rewrites_match_concl_with_all_tac all_tac assume_tac) thml
+ end
+
+(* Same as {!sg_dep_rewrite_tac} but this time we apply rewrites only in the conclusion
+ of the goal (not the assumptions).
+ *)
+fun sg_dep_rewrite_goal_tac (th : thm) =
+ let
+ (* Split the theorem *)
+ val thml = split_rewrite_thm th
+ in
+ MAP_EVERY (apply_dep_rewrites_match_concl_with_goal_tac all_tac assume_tac) thml
+ end
+
+(*
+val (asms, g) = ([
+ “u32_to_int z = u32_to_int i − u32_to_int (int_to_u32 1)”,
+ “u32_to_int (int_to_u32 2) = 2”
+], “T”)
+
+apply_dep_rewrites_match_concl_tac
+ (FULL_SIMP_TAC simpLib.empty_ss all_integer_bounds >> COOPER_TAC)
+ (fn th => FULL_SIMP_TAC simpLib.empty_ss [th])
+ int_to_u32_id
+*)
+
+(* Attempt to apply dependent rewrites with a theorem by matching its
+ first premise with subterms of the goal.
+
+ Leaves the premises as subgoals if [prove_premise] doesn't prove them.
+ *)
+fun apply_dep_rewrites_match_first_premise_tac
+ (prove_premise : tactic) (then_tac : thm_tactic) (th : thm) : tactic =
+ fn (asms, g) =>
+ let
+ (* Discharge the assumptions so that the goal is one single term *)
+ val thms = inst_match_first_premise_in_terms th (g :: asms);
+ val thms = map thm_to_conj_implies thms;
+ fun apply_tac th =
+ let
+ val th = thm_to_conj_implies th;
+ in
+ sg_premise_then prove_premise then_tac th
+ end;
+ in
+ (* Apply each theorem *)
+ MAP_EVERY apply_tac thms (asms, g)
+ end
+
end