249 lines
8.1 KiB
Plaintext
249 lines
8.1 KiB
Plaintext
(*
|
|
* Copyright 2018, Data61, CSIRO
|
|
*
|
|
* This software may be distributed and modified according to the terms of
|
|
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
|
|
* See "LICENSE_BSD2.txt" for details.
|
|
*
|
|
* @TAG(DATA61_BSD)
|
|
*)
|
|
|
|
theory Trace_Schematic_Insts_Test
|
|
imports
|
|
Lib.Trace_Schematic_Insts
|
|
begin
|
|
|
|
text \<open>
|
|
Trace the schematic variables and types that a method instantiates.
|
|
This only works for the variables already in the proof goal; new
|
|
variables introduced by the traced method are not tracked.
|
|
\<close>
|
|
|
|
experiment begin
|
|
section \<open>Examples\<close>
|
|
|
|
text \<open>Schematic variables\<close>
|
|
lemma "\<lbrakk> \<forall>x. P x \<rbrakk> \<Longrightarrow> P x"
|
|
apply (drule spec) \<comment> \<open>introduces schematic var "?x"\<close>
|
|
apply (trace_schematic_insts \<open>assumption\<close>)
|
|
done
|
|
|
|
definition foo :: "'a \<Rightarrow> bool"
|
|
where "foo x = True"
|
|
|
|
lemma fooI1:
|
|
"foo 0 \<Longrightarrow> foo x"
|
|
by (simp add: foo_def)
|
|
|
|
lemma fooI2:
|
|
"foo x \<Longrightarrow> foo 0"
|
|
by (simp add: foo_def)
|
|
|
|
lemma fooI2':
|
|
"foo x \<Longrightarrow> foo (0 :: nat)"
|
|
by (erule fooI2)
|
|
|
|
text \<open>Schematic type variables\<close>
|
|
lemma "foo x \<Longrightarrow> foo y"
|
|
apply (rule fooI1) \<comment> \<open>introduces schematic type "0 :: ?'a"\<close>
|
|
apply (trace_schematic_insts \<open>erule fooI2'\<close>)
|
|
done
|
|
|
|
text \<open>When backtracking, every recursive invocation is traced\<close>
|
|
lemma "\<lbrakk> \<forall>x. Q x \<longrightarrow> R x; \<forall>x. P x \<longrightarrow> Q x; P x; P y \<longrightarrow> R x \<rbrakk> \<Longrightarrow> R x"
|
|
apply (drule spec)
|
|
apply (drule spec)
|
|
text \<open>For more clarity, methods can be named\<close>
|
|
apply (trace_schematic_insts impE1 \<open>erule impE\<close>,
|
|
trace_schematic_insts impE2 \<open>erule impE\<close>,
|
|
(trace_schematic_insts "try assumption" \<open>assumption\<close>)+; fail)
|
|
done
|
|
|
|
text \<open>Interactive example\<close>
|
|
ML \<open>
|
|
fun trace_resolve_tac ctxt =
|
|
Trace_Schematic_Insts.trace_schematic_insts_tac ctxt
|
|
(Trace_Schematic_Insts.default_rule_report ctxt "demo title")
|
|
(fn t => resolve_tac ctxt [t])
|
|
\<close>
|
|
lemma
|
|
assumes Pf: "\<And>f (x :: nat). Q f \<Longrightarrow> P x \<Longrightarrow> P (f x)"
|
|
assumes Q: "Q ((*) 2)"
|
|
assumes P: "P a"
|
|
shows "\<exists>x. P (x + a + a)"
|
|
apply (tactic \<open>trace_resolve_tac @{context} @{thm exI} 1\<close>)
|
|
apply (trace_schematic_insts \<open>subst add_0\<close>)
|
|
|
|
\<comment>\<open>
|
|
This picks *some* instantiation of `f` in @{thm Pf}. The first one is
|
|
@{term "\<lambda>a. a"}, which isn't what we want.
|
|
\<close>
|
|
apply (tactic \<open>trace_resolve_tac @{context} @{thm Pf} 1\<close>)
|
|
\<comment>\<open>
|
|
This picks the *next* instantiation of `f`, in this case @{term "\<lambda>a. a + a"}
|
|
Notice that the reporting callback gets called with the new instantiations!
|
|
\<close>
|
|
back
|
|
|
|
apply (tactic \<open>
|
|
Trace_Schematic_Insts.trace_schematic_insts_tac
|
|
@{context}
|
|
(Trace_Schematic_Insts.default_rule_report @{context} "demo title")
|
|
(fn t => EqSubst.eqsubst_tac @{context} [0] [t])
|
|
@{thm mult_2[symmetric]} 1
|
|
\<close>)
|
|
apply (tactic \<open>trace_resolve_tac @{context} @{thm Q} 1\<close>)
|
|
apply (tactic \<open>trace_resolve_tac @{context} @{thm P} 1\<close>)
|
|
done
|
|
|
|
section \<open>Tests\<close>
|
|
|
|
ML \<open>
|
|
fun trace_schematic_assert ctxt test_name tac expected_vars expected_tvars =
|
|
let
|
|
fun skip_dummy_state tac = fn st =>
|
|
case Thm.prop_of st of
|
|
Const (@{const_name Pure.prop}, _) $
|
|
(Const (@{const_name Pure.term}, _) $ Const (@{const_name Pure.dummy_pattern}, _)) =>
|
|
Seq.succeed st
|
|
| _ => tac st
|
|
|
|
fun check insts =
|
|
if expected_vars = #terms insts andalso expected_tvars = #typs insts then () else
|
|
error ("Trace_Schematic_Insts failed test: " ^ test_name)
|
|
|
|
in Method.NO_CONTEXT_TACTIC ctxt
|
|
(Trace_Schematic_Insts.trace_schematic_insts (SIMPLE_METHOD tac) check [])
|
|
|> skip_dummy_state
|
|
end
|
|
\<close>
|
|
|
|
text \<open>Schematic variables\<close>
|
|
lemma "\<lbrakk> \<forall>x. P x \<rbrakk> \<Longrightarrow> P x"
|
|
apply (drule spec)
|
|
apply (tactic \<open>let
|
|
val alpha = TFree ("'a", @{sort type})
|
|
val expected_vars = [(Var (("x", 0), alpha), Free ("x", alpha))]
|
|
val expected_tvars = []
|
|
in trace_schematic_assert @{context}
|
|
"basic Var test" (assume_tac @{context} 1)
|
|
expected_vars expected_tvars
|
|
end\<close>)
|
|
done
|
|
|
|
text \<open>Schematic type variables\<close>
|
|
lemma "foo x \<Longrightarrow> foo y"
|
|
apply (rule fooI1)
|
|
apply (tactic \<open>let
|
|
val expected_vars = []
|
|
val expected_tvars = [(TVar (("'a", 0), @{sort zero}), @{typ nat})]
|
|
in trace_schematic_assert
|
|
@{context}
|
|
"basic TVar test"
|
|
(eresolve_tac @{context} @{thms fooI2'} 1)
|
|
expected_vars expected_tvars
|
|
end\<close>)
|
|
done
|
|
|
|
|
|
ML \<open>
|
|
fun trace_schematic_resolve_tac_assert ctxt test_name thm expected_rule_insts expected_proof_insts =
|
|
let
|
|
fun check rule_insts proof_insts =
|
|
if expected_rule_insts = rule_insts andalso expected_proof_insts = proof_insts
|
|
then ()
|
|
else
|
|
let
|
|
val _ = tracing (@{make_string} (rule_insts, proof_insts))
|
|
in error ("Trace_Schematic_Insts failed test: " ^ test_name) end
|
|
fun tactic thm = resolve_tac ctxt [thm]
|
|
in HEADGOAL (Trace_Schematic_Insts.trace_schematic_insts_tac ctxt check tactic thm)
|
|
end
|
|
\<close>
|
|
|
|
text \<open>Simultaneous rule and goal instantiations\<close>
|
|
lemma "\<exists>a. foo (a :: nat)"
|
|
apply (rule exI)
|
|
apply (tactic \<open>
|
|
let
|
|
val a' = TVar (("'a", 0), @{sort type})
|
|
val b' = TVar (("'b", 0), @{sort zero})
|
|
val a'' = TVar (("'a", 2), @{sort type})
|
|
val expected_rule_vars = [
|
|
(Var (("x", 0), a'), Var(("x", 2), a''))
|
|
]
|
|
val expected_rule_tvars = [
|
|
(a', a''),
|
|
(b', @{typ nat})
|
|
]
|
|
val expected_goal_vars = [
|
|
(Var (("a", 0), @{typ nat}), @{term "0 :: nat"})
|
|
]
|
|
in
|
|
trace_schematic_resolve_tac_assert
|
|
@{context}
|
|
"basic rule tracing"
|
|
@{thm fooI2}
|
|
{bounds = [], terms = expected_rule_vars, typs = expected_rule_tvars}
|
|
{bounds = [], terms = expected_goal_vars, typs = []}
|
|
end
|
|
\<close>)
|
|
by (simp add: foo_def)
|
|
|
|
text \<open>Rule instantiations with bound variables\<close>
|
|
lemma "\<And>X. X \<and> Y \<Longrightarrow> Y \<and> X"
|
|
apply (tactic \<open>
|
|
let
|
|
val expected_rule_bounds = [("X", @{typ bool})]
|
|
val expected_rule_vars = [
|
|
(Var (("P", 0), @{typ bool}), @{term "\<lambda>X :: bool. Y :: bool"}),
|
|
(Var (("Q", 0), @{typ bool}), @{term "\<lambda>X :: bool. X :: bool"})
|
|
]
|
|
in
|
|
trace_schematic_resolve_tac_assert
|
|
@{context}
|
|
"rule tracing with bound variables"
|
|
@{thm conjI}
|
|
{bounds = expected_rule_bounds, terms = expected_rule_vars, typs = []}
|
|
{bounds = [], terms = [], typs = []}
|
|
end
|
|
\<close>)
|
|
by simp+
|
|
|
|
text \<open>Rule instantiations with function terms\<close>
|
|
lemma "\<exists>f. \<forall>x. f x = x"
|
|
apply (intro exI allI)
|
|
apply (rule fun_cong)
|
|
apply (tactic \<open>
|
|
let
|
|
val a' = TVar (("'a", 0), @{sort type})
|
|
\<comment>\<open>
|
|
New lambda abstraction gets an anonymous variable name. Usually rendered as
|
|
@{term "\<lambda>x a. a"}.
|
|
\<close>
|
|
val lambda = Abs ("x", @{typ 'a}, Abs ("", @{typ 'a}, Bound 0))
|
|
|
|
val expected_rule_bounds = [("x", @{typ 'a})]
|
|
val expected_rule_vars = [
|
|
(Var (("t", 0), a'), lambda)
|
|
]
|
|
val expected_rule_typs = [
|
|
(a', @{typ "'a \<Rightarrow> 'a"})
|
|
]
|
|
val expected_goal_vars = [
|
|
(Var (("f", 2), @{typ "'a \<Rightarrow> 'a \<Rightarrow> 'a"}), lambda)
|
|
]
|
|
in
|
|
trace_schematic_resolve_tac_assert
|
|
@{context}
|
|
"rule tracing with function term instantiations"
|
|
@{thm refl}
|
|
{bounds = expected_rule_bounds, terms = expected_rule_vars, typs = expected_rule_typs}
|
|
{bounds = [], terms = expected_goal_vars, typs = []}
|
|
end
|
|
\<close>)
|
|
done
|
|
|
|
end
|
|
|
|
end |