File ‹context_tactical.ML›
infix 1 THEN_CTXT THEN_CTXT' THEN_MAYBE_CTXT THEN_MAYBE_CTXT' THEN_ALL_NEW_CTXT
THEN_MAYBE_ALL_NEW_CTXT THEN_MAYBE_SOLVE_ABORT_ALL_NEW_CTXT;
infix 0 ORELSE_CTXT ORELSE_CTXT' SOLVED_ORELSE_CTXT' SOLVED_ABORT_ORELSE_CTXT' APPEND_CTXT APPEND_CTXT';
infix 0 THEN_ELSE_CTXT THEN_ELSE_CTXT'
signature BASIC_INFIX_CONTEXT_TACTIC =
sig
val THEN_CTXT: context_tactic * context_tactic -> context_tactic
val THEN_MAYBE_CTXT: context_tactic * context_tactic -> context_tactic
val ORELSE_CTXT: context_tactic * context_tactic -> context_tactic
val APPEND_CTXT: context_tactic * context_tactic -> context_tactic
val THEN_ELSE_CTXT: context_tactic * (context_tactic * context_tactic) -> context_tactic
val THEN_CTXT': (int -> context_tactic) * (int -> context_tactic) -> (int -> context_tactic)
val THEN_MAYBE_CTXT': (int -> context_tactic) * (int -> context_tactic) -> (int -> context_tactic)
val ORELSE_CTXT': (int -> context_tactic) * (int -> context_tactic) -> (int -> context_tactic)
val SOLVED_ORELSE_CTXT': ((int -> context_tactic) * (Proof.context -> Proof.context -> Proof.context)) * (int -> context_tactic) -> (int -> context_tactic)
val SOLVED_ABORT_ORELSE_CTXT': ((int -> context_tactic) * (Proof.context -> Proof.context -> Proof.context)) * (int -> context_tactic) -> (int -> context_tactic)
val APPEND_CTXT': (int -> context_tactic) * (int -> context_tactic) -> (int -> context_tactic)
val THEN_ELSE_CTXT': (int -> context_tactic) * ((int -> context_tactic) * (int -> context_tactic)) -> int -> context_tactic
val THEN_ALL_NEW_CTXT: (int -> context_tactic) * (int -> context_tactic) -> int -> context_tactic
val THEN_MAYBE_ALL_NEW_CTXT: (int -> context_tactic) * (int -> context_tactic) -> int -> context_tactic
val THEN_MAYBE_SOLVE_ABORT_ALL_NEW_CTXT: (int -> context_tactic) * (int -> context_tactic) -> int -> context_tactic
end
signature INFIX_CONTEXT_TACTIC =
sig
include BASIC_INFIX_CONTEXT_TACTIC
val all_tac : context_tactic
val no_tac : context_tactic
val print_tac: string -> context_tactic
val gen_print_tac: (Proof.context -> int) -> (int * int) -> (Proof.context -> string) -> context_tactic
val verbose_print_tac:(int * int) -> (Proof.context -> string) -> context_tactic
val print_subgoal_tac: string -> int -> context_tactic
val gen_print_subgoal_tac: (Proof.context -> int) -> (int * int) -> ((Proof.context * int) -> string) -> int -> context_tactic
val verbose_print_subgoal_tac: (int * int) -> ((Proof.context * int) -> string) -> int -> context_tactic
end
signature MORE_CONTEXT_TACTIC =
sig
include CONTEXT_TACTIC
include INFIX_CONTEXT_TACTIC
val WITH_CONTEXT: (Proof.context -> context_tactic) -> context_tactic
val CONTEXT_TACTIC': (Proof.context -> tactic) -> context_tactic
val CSUBGOAL: ((cterm * int) -> context_tactic) -> int -> context_tactic
val SINGLE: context_tactic -> context_state -> context_state option
val FIRST: context_tactic list -> context_tactic
val FIRST': (int -> context_tactic) list -> int -> context_tactic
val SOLVED_FIRST': (Proof.context -> Proof.context -> Proof.context) -> (int -> context_tactic) list -> int -> context_tactic
val SOLVED_ABORT_FIRST': (Proof.context -> Proof.context -> Proof.context) -> (int -> context_tactic) list -> int -> context_tactic
val ONLY_SCHEMATIC_GOAL: bool -> (int -> context_tactic) -> int -> context_tactic
val resolve_tac: thm list -> int -> context_tactic
val only_schematic_resolve_tac: bool -> thm list -> int -> context_tactic
val resolve_assm_tac: thm list -> int -> context_tactic
val resolve_consumes_assm_tac: thm list -> int -> context_tactic
val only_schematic_resolve_consumes_assm_tac: bool -> thm list -> int -> context_tactic
val binding_resolve_tac: (binding * thm) list -> (binding * (int -> context_tactic)) list
val assm_tac: int -> context_tactic
val compose_tac: (bool * thm * int) -> int -> context_tactic
type ctxt_cache = {
lookup: Proof.context -> cterm -> int -> context_tactic,
insert: (Timing.timing * int * int) -> thm -> Proof.context -> Proof.context,
propagate: Proof.context -> Proof.context -> Proof.context
}
val no_cache: ctxt_cache
val trace_cache: ctxt_cache
val cache_deepen_tac: (Proof.context -> int) -> ctxt_cache ->
(cterm -> (binding * (int -> context_tactic)) list) -> int -> context_tactic
val concat_goal_funs: (cterm -> 'a list) list -> cterm -> 'a list
end
structure Infix_Context_Tactic: INFIX_CONTEXT_TACTIC =
struct
val all_tac = CONTEXT_TACTIC all_tac
val no_tac = CONTEXT_TACTIC no_tac
val print_tac = fn msg => fn (ctxt, st) =>
Context_Tactic.CONTEXT_TACTIC (print_tac ctxt msg) (ctxt, st)
val print_subgoal_tac = fn msg => fn i => fn (ctxt, st) =>
Context_Tactic.CONTEXT_TACTIC (Utils.print_subgoal_tac msg ctxt i) (ctxt, st)
fun gen_print_tac verbose (short, long) msg (ctxt, st) =
let
val level = verbose ctxt
in
if level >= long andalso long >= 0
then print_tac (msg ctxt) (ctxt, st)
else (if level >= short andalso short >= 0 then tracing (msg ctxt) else (); all_tac (ctxt, st))
end
val verbose_print_tac = gen_print_tac (fn ctxt => Config.get ctxt Utils.verbose)
fun gen_print_subgoal_tac verbose (short, long) msg i (ctxt, st) =
let
val level = verbose ctxt
in
if level >= long andalso long >= 0
then print_subgoal_tac (msg (ctxt, i)) i (ctxt, st)
else (if level >= short andalso short >= 0 then tracing (msg (ctxt, i)) else (); all_tac (ctxt, st))
end
val verbose_print_subgoal_tac = gen_print_subgoal_tac (fn ctxt => Config.get ctxt Utils.verbose)
fun (tac1 THEN_CTXT tac2) (ctxt, st) = Seq.maps_results tac2 (tac1 (ctxt, st));
fun (tac1 ORELSE_CTXT tac2) (ctxt, st) =
(case Seq.pull (tac1 (ctxt, st)) of
NONE => tac2 (ctxt, st)
| some => Seq.make (fn () => some));
fun (tac1 THEN_CTXT' tac2) x = tac1 x THEN_CTXT tac2 x;
fun (tac1 ORELSE_CTXT' tac2) x = tac1 x ORELSE_CTXT tac2 x;
fun (tac1 APPEND_CTXT tac2) (ctxt, st) =
Seq.append (tac1 (ctxt, st)) (Seq.make(fn()=> Seq.pull (tac2 (ctxt,st))));
fun (tac1 APPEND_CTXT' tac2) x = tac1 x APPEND_CTXT tac2 x;
fun (tac THEN_ELSE_CTXT (tac1, tac2)) (ctxt, st) =
(case Seq.pull (tac (ctxt, st)) of
NONE => tac2 (ctxt, st)
| some => Seq.maps_results tac1 (Seq.make (fn () => some)));
fun (tac THEN_ELSE_CTXT' (tac1, tac2)) x = tac x THEN_ELSE_CTXT (tac1 x, tac2 x);
fun (tac1 THEN_MAYBE_CTXT tac2) = tac1 THEN_ELSE_CTXT (tac2, all_tac)
fun (tac1 THEN_MAYBE_CTXT' tac2) = tac1 THEN_ELSE_CTXT' (tac2, K all_tac)
fun ((tac1, propagate) SOLVED_ORELSE_CTXT' tac2) i (ctxt, st) =
(case Seq.pull (Seq.filter_results (tac1 i (ctxt, st))) of
NONE => tac2 i (ctxt, st)
| SOME ((ctxt', st'), _) => if Thm.nprems_of st' < Thm.nprems_of st then all_tac (propagate ctxt' ctxt, st') else tac2 i (propagate ctxt' ctxt, st))
fun aborted i st =
try (Utils.concl_of_subgoal_open o curry Logic.nth_prem i o Thm.prop_of) st = SOME @{term "PROP FALSE"}
fun unsolved i st =
is_some (try (curry Logic.nth_prem i o Thm.prop_of) st)
fun aborted_result i (Seq.Result (_, st)) = aborted i st
| aborted_result _ _ = false
fun unsolved_result i (Seq.Result (ctxt, st)) = if unsolved i st then SOME (ctxt, st) else NONE
| unsolved_result _ _ = NONE
fun ((tac1, propagate) SOLVED_ABORT_ORELSE_CTXT' tac2) i (ctxt, st) =
(case Seq.pull (Seq.filter_results (tac1 i (ctxt, st))) of
NONE => tac2 i (ctxt, st)
| SOME ((ctxt', st'), _) =>
if Thm.nprems_of st' < Thm.nprems_of st then
all_tac (ctxt', st')
else
if Thm.nprems_of st' = Thm.nprems_of st andalso aborted i st' then
verbose_print_subgoal_tac (4, 6) (fn _ => "SOLVED_ABORT_ORELSE_CTXT' unsolved subgoal " ^ string_of_int i) i
(ctxt', st')
else
tac2 i (propagate ctxt' ctxt, st))
fun INTERVAL_RESULT f (i: int) j cst =
if i > j then all_tac cst
else Seq.maps_results (INTERVAL_RESULT f i (j - 1)) (f j cst);
fun INTERVAL_SOLVE_ABORT_RESULT f (i: int) j cst =
if i > j then all_tac cst
else
case Seq.pull (f j cst) of
NONE => Seq.make (fn () => NONE)
| SOME (rcst, rcst') =>
if aborted_result j rcst then verbose_print_tac (4, ~1) (fn _ => "INTERVAL_SOLVE_ABORT_RESULT aborted subgoal " ^ string_of_int j) cst
else (case unsolved_result j rcst of
SOME cst => verbose_print_subgoal_tac (4, 6) (fn _ => "INTERVAL_SOLVE_ABORT_RESULT unsolved subgoal " ^ string_of_int j) j cst
| NONE => Seq.maps_results (INTERVAL_SOLVE_ABORT_RESULT f i (j - 1))
(Seq.make (fn () => SOME (rcst, rcst'))))
fun (tac1 THEN_ALL_NEW_CTXT tac2) i (ctxt, st) =
(ctxt, st) |> (tac1 i THEN_CTXT (fn (ctxt', st') =>
(ctxt', st') |> INTERVAL_RESULT tac2 i (i + Thm.nprems_of st' - Thm.nprems_of st)));
fun (tac1 THEN_MAYBE_ALL_NEW_CTXT tac2) i (ctxt, st) =
(ctxt, st) |> (tac1 i THEN_MAYBE_CTXT (fn (ctxt', st') =>
(ctxt', st') |> INTERVAL_RESULT tac2 i (i + Thm.nprems_of st' - Thm.nprems_of st)));
fun (tac1 THEN_MAYBE_SOLVE_ABORT_ALL_NEW_CTXT tac2) i (ctxt, st) =
(ctxt, st) |> (tac1 i THEN_MAYBE_CTXT (fn (ctxt', st') =>
(ctxt', st') |> INTERVAL_SOLVE_ABORT_RESULT tac2 i (i + Thm.nprems_of st' - Thm.nprems_of st)));
end
structure CT: MORE_CONTEXT_TACTIC =
struct
open Context_Tactic
open Infix_Context_Tactic
fun WITH_CONTEXT ctxt_tac (ctxt, st) = ctxt_tac ctxt (ctxt, st)
fun CONTEXT_TACTIC' tac : context_tactic =
fn (ctxt, st) => TACTIC_CONTEXT ctxt (tac ctxt st);
fun CSUBGOAL goalfun i (ctxt, st) =
(case SOME (Thm.cprem_of st i) handle THM _ => NONE of
SOME goal => goalfun (goal, i) (ctxt, st)
| NONE => Seq.empty);
fun ONLY_SCHEMATIC_GOAL false tac = tac
| ONLY_SCHEMATIC_GOAL true tac =
CSUBGOAL (fn (g, i) =>
if is_schematic (Thm.term_of g) then tac i else no_tac )
val resolve_tac = fn thms => fn i => fn (ctxt, st) =>
Context_Tactic.CONTEXT_TACTIC (resolve_tac ctxt thms i) (ctxt, st)
fun only_schematic_resolve_tac flag thms = ONLY_SCHEMATIC_GOAL flag (resolve_tac thms)
val resolve_assm_tac = fn thms => fn i => fn (ctxt, st) =>
Context_Tactic.CONTEXT_TACTIC (Utils.resolve_assm_tac ctxt thms i) (ctxt, st)
val resolve_consumes_assm_tac = fn thms => fn i => fn (ctxt, st) =>
Context_Tactic.CONTEXT_TACTIC (Utils.resolve_consumes_assm_tac ctxt thms i) (ctxt, st)
fun only_schematic_resolve_consumes_assm_tac flag thms = ONLY_SCHEMATIC_GOAL flag (resolve_consumes_assm_tac thms)
val binding_resolve_tac = map (apsnd (resolve_tac o single))
val assm_tac = fn i => fn (ctxt, st) =>
Context_Tactic.CONTEXT_TACTIC (Method.assm_tac ctxt i) (ctxt, st)
val compose_tac = fn args => fn i => fn (ctxt, st) =>
Context_Tactic.CONTEXT_TACTIC (compose_tac ctxt args i) (ctxt, st)
fun SINGLE tacf = Option.map fst o Seq.pull o Seq.filter_results o tacf
fun FIRST tacs = fold_rev (curry op ORELSE_CTXT) tacs no_tac;
fun FIRST' tacs = fold_rev (curry op ORELSE_CTXT') tacs (K no_tac);
fun SOLVED_FIRST' propagate tacs =
case tacs of [] => K all_tac
| [tac] => tac
| _ => foldr1 (fn (tac1, tac2) => (tac1, propagate) SOLVED_ORELSE_CTXT' tac2) tacs;
fun SOLVED_ABORT_FIRST' propagate tacs =
case tacs of [] => K all_tac
| [tac] => tac
| _ => foldr1 (fn (tac1, tac2) => (tac1, propagate) SOLVED_ABORT_ORELSE_CTXT' tac2) tacs;
fun subgoal_prove ctxt goal ctxt_tac =
let
val st = Goal.init goal
in
case SINGLE ctxt_tac (ctxt, st) of
NONE => (warning ("subgoal_prove: empty result sequence (context not propagated).\n To propagate result leave an unfinished goal instead: " ^ Syntax.string_of_term ctxt (Thm.term_of goal));
(ctxt, Goal.conclude st, 1))
| SOME (ctxt', st') => let val n = Thm.nprems_of st'; val st' = Goal.conclude st' in (ctxt', st', n) end
end
type ctxt_cache = {
lookup: Proof.context -> cterm -> int -> context_tactic,
insert: (Timing.timing * int * int) -> thm -> Proof.context -> Proof.context,
propagate: Proof.context -> Proof.context -> Proof.context
}
val no_cache:ctxt_cache = {lookup = K (K (K no_tac)), insert = K (K I), propagate = fn current => fn old => current}
val trace_cache:ctxt_cache = {
lookup = fn ctxt => fn goal => (tracing ("lookup: " ^ Syntax.string_of_term ctxt (Thm.term_of goal)); K no_tac),
insert = fn timing => fn thm => fn ctxt =>
let
val thm' = thm |> Thm.forall_elim_vars ((Thm.maxidx_of thm) + 1) |> zero_var_indexes
val _ = tracing ("insert: " ^ Thm.string_of_thm ctxt thm')
in
ctxt
end,
propagate = fn current => fn old => current
}
local
fun matcher context ps os =
let
val thy = Context.theory_of context
in
if length ps <> length os then NONE
else try (fold (Pattern.match thy) (ps ~~ os)) (Vartab.empty, Vartab.empty) |> Option.map (fn (tyenv, tenv) =>
Envir.Envir {maxidx = ~1, tenv = tenv, tyenv = tyenv})
end
in
fun pretty_insts ctxt open_goals solved_concls =
if null open_goals then [] else
let
val title = "goal instantiation:";
val goals' = (map Thm.term_of open_goals)
fun prt_inst env =
if Envir.is_empty env then []
else
let
val Envir.Envir {tyenv, tenv, ...} = env;
val prt_type = Syntax.pretty_typ ctxt;
val prt_term = Syntax.pretty_term ctxt;
fun instT v =
let
val T = TVar v;
val T' = Envir.subst_type tyenv T;
in if T = T' then NONE else SOME (prt_type T, prt_type T') end;
fun subst_term_closure t =
let val t' = Envir.subst_term (tyenv, tenv) (Envir.subst_term_types tyenv t)
in if t aconv t' then t else subst_term_closure t' end
fun inst v =
let
val t = Var v;
val t' = subst_term_closure t
in if t aconv t' then NONE else SOME (prt_term t, prt_term t') end;
fun inst_pair (x, y) = Pretty.item [x, Pretty.str " ↝", Pretty.brk 1, y];
val prts =
(Term.add_tvars (hd goals') [] |> sort Term_Ord.tvar_ord |> map_filter instT) @
(Term.add_vars (hd goals') [] |> sort Term_Ord.var_ord |> map_filter inst);
in if null prts then [] else [Pretty.big_list title (map inst_pair prts)] end;
fun no_new_vars (goal, solved) =
subset (op =) (Term.add_tvars solved [], (Term.add_tvars goal [])) andalso
subset (op =) (Term.add_vars solved [], (Term.add_vars goal []))
val (goals1, solved_concls1) = split_list (filter_out (no_new_vars) (goals' ~~ solved_concls))
fun goal_matcher () =
matcher (Context.Proof ctxt) goals1 solved_concls1
fun failure msg = (warning (title ^ " " ^ msg);
[]);
in
(case goal_matcher () of
SOME env => prt_inst env
| NONE => failure "match failed")
end
end
fun gen_cache_deepen_tac (depth, alt, total, path, goals, concls) verbose (cache: ctxt_cache) get_step_tacs i =
i |> CSUBGOAL (fn (goal, i) => fn (ctxt, st) =>
let
fun compose_subgoal n thms = FIRST' (map (fn thm => compose_tac (false, thm, n)) thms)
val nprems = Thm.nprems_of st
val concl = Logic.unprotect (Thm.concl_of st)
fun trace_inst more_goals more_concls =
if null goals then () else
Utils.verbose_msg 3 ctxt (fn _ => Pretty.string_of (Pretty.paragraph
(pretty_insts ctxt (goals @ more_goals) (drop 1 concls @ more_concls))))
fun msg_common alt total = fn _ => "cache_deepen_tac (depth " ^ string_of_int depth ^
", subgoals " ^ string_of_int nprems ^
", tactic " ^ string_of_int alt ^ " of " ^ string_of_int total ^
", path " ^ @{make_string} path ^
")"
fun subgoal_msg alt total str i = fn _ => msg_common alt total () ^ "\n " ^ str ^ " " ^ string_of_int i
fun trace_inst_tac more_goals more_concls = (fn st => (trace_inst more_goals more_concls; all_tac st))
fun trace_inst_after_tac goal st_open st_solved st =
let
val concl_solved = Thm.concl_of st_solved
val concl_orig = Logic.unprotect (Thm.concl_of st_open)
in trace_inst_tac [goal] [concl_orig, concl_solved] st end
fun cached_goal goal i (ctxt, st) =
let
val cache_tac = #lookup cache ctxt goal
in
(ctxt, st) |> (
cache_tac i THEN_ELSE_CTXT
(gen_print_tac verbose (1, ~1) (subgoal_msg alt total "cache hit on subgoal:" i),
gen_print_tac verbose (3, ~1) (subgoal_msg alt total "cache miss on subgoal:" i)))
end
fun new_goal goal total (alt, (b, tac)) i (ctxt, st) =
let
val alt = alt + 1;
fun trace_tac b tac = tac THEN_ELSE_CTXT'
(fn i => gen_print_tac verbose (1, ~1) (subgoal_msg alt total ("tactic " ^ More_Binding.here b ^ " applied to subgoal:") i),
fn i => gen_print_tac verbose (3, ~1) (subgoal_msg alt total ("tactic " ^ More_Binding.here b ^ " failed on subgoal:") i) THEN_CTXT no_tac)
val (timing, (ctxt', st', nsubgoals)) = Timing.timing (subgoal_prove ctxt goal) (1 |> (
trace_tac b tac THEN_MAYBE_SOLVE_ABORT_ALL_NEW_CTXT (gen_cache_deepen_tac (depth + 1, alt, total, path @ [alt], goals @ [goal], concls @ [concl]) verbose cache get_step_tacs )))
val subgoal_was_solved = nsubgoals = 0
fun compose_print_subgoal l str =
compose_subgoal nsubgoals [st'] i THEN_CTXT
gen_print_tac verbose (l, ~1) (subgoal_msg alt total str i) THEN_CTXT
(if subgoal_was_solved then trace_inst_after_tac goal st st' else all_tac)
in
if subgoal_was_solved
then (#insert cache (timing, total, alt) st' ctxt', st) |> compose_print_subgoal 1 "solved subgoal:"
else (#insert cache (timing, total, alt) (Thm.instantiate' [] [SOME goal] @{thm ex_falso_quodlibet}) ctxt', st)
|> compose_print_subgoal 3 "failed on subgoal:"
end
val fresh_proof = CSUBGOAL (fn (goal, i) =>
let
val step_tacs = get_step_tacs goal
val n = length step_tacs
val long = if n = 0 then 2 else ~1
in
gen_print_subgoal_tac verbose (2, long) (subgoal_msg alt total (string_of_int n ^ " tactic(s) for subgoal:") i) i THEN_CTXT
SOLVED_FIRST' (#propagate cache) (map_index (new_goal goal n) step_tacs) i
end)
in
(i, (ctxt, st)) |> uncurry (
gen_print_subgoal_tac verbose (4, 4) (msg_common alt total) THEN_CTXT' (K (trace_inst_tac [] [concl])) THEN_CTXT' (
(cached_goal goal, #propagate cache)
SOLVED_ABORT_ORELSE_CTXT'
fresh_proof))
end)
fun cache_deepen_tac verbose cache get_step_tacs i =
i |> CSUBGOAL (fn (goal, i) => gen_cache_deepen_tac (0, ~1, 0, [], [], []) verbose cache get_step_tacs i)
fun concat_goal_funs funs goal =
[] |> fold_rev (curry (op @)) (map (rev_app goal) funs)
end
structure Basic_Infix_Context_Tactic: BASIC_INFIX_CONTEXT_TACTIC = Infix_Context_Tactic;
open Basic_Infix_Context_Tactic;