(* Title: HOL/Eisbach/match_method.ML Author: Daniel Matichuk, NICTA/UNSW
Setup for "match" proof method. It provides basic fact/term matching in addition to premise/conclusion matching through Subgoal.focus, and binds fact names from matches as well as term patterns within matches.
*)
signature MATCH_METHOD = sig val focus_schematics: Proof.context -> Envir.tenv val focus_params: Proof.context -> term list (* FIXME proper ML interface for the main thing *) end
structure Match_Method : MATCH_METHOD = struct
(* Filter premises by predicate, with premise order; recovers premise order afterwards.*) fun filter_prems_tac' ctxt prem = let funThen NONE tac = SOME tac
| Then (SOME tac) tac' = SOME (tac THEN' tac'); fun thins idxH (tac, n, i) = if prem idxH then (tac, n + 1 , i) else (Then tac (rotate_tac n THEN' eresolve_tac ctxt [thin_rl]), 0, i + n); in
SUBGOAL (fn (goal, i) => letval idxHs = tag_list 0 (Logic.strip_assums_hyp goal) in
(case fold thins idxHs (NONE, 0, 0) of
(NONE, _, _) => no_tac
| (SOME tac, _, n) => tac i THEN rotate_tac (~ n) i) end) end;
datatype match_kind =
Match_Term of term Item_Net.T
| Match_Fact of thm Item_Net.T
| Match_Concl
| Match_Prems ofbool;
fun nameable_match m = (case m of Match_Fact _ => true | Match_Prems _ => true | _ => false); fun prop_match m = (case m of Match_Term _ => false | _ => true);
val bound_thm : (thm, binding) Parse_Tools.parse_val parser =
Parse_Tools.parse_thm_val Parse.binding;
val bound_term : (term, binding) Parse_Tools.parse_val parser =
Parse_Tools.parse_term_val Parse.binding;
val for_fixes = Scan.optional (\<^keyword>\<open>for\<close> |-- fixes) [];
fun pos_of dyn = Parse_Tools.the_parse_val dyn |> Binding.pos_of;
(*FIXME: Dynamic facts modify the background theory, so we have to resort
to token replacement for matched facts. *) val dynamic_fact =
Scan.lift bound_thm -- Attrib.opt_attribs;
fun reject_extra_free (Free (x, _)) () = if Variable.is_fixed ctxt5 x then () else error ("Illegal use of free (unfixed) variable " ^ quote x)
| reject_extra_free _ () = (); val _ = (fold o fold_aterms) reject_extra_free pats ();
val binds = map (fn (b, _) => Option.map (fn (b, att) => (Parse_Tools.the_parse_val b, att)) b) ts;
fun upd_ctxt (SOME (b, att)) pat (tms, ctxt) = let val ([nm], ctxt') =
Variable.variant_fixes [Name.internal (Binding.name_of b)] ctxt; val abs_nms = Term.strip_all_vars pat;
val param_thm = map (Drule.mk_term o Thm.cterm_of ctxt' o Free) abs_nms
|> Conjunction.intr_balanced
|> Drule.generalize (Names.empty, Names.make_set (map fst abs_nms))
|> Thm.tag_free_dummy;
val atts = map (Attrib.attribute ctxt') att; val (param_thm', ctxt'') = Thm.proof_attributes atts param_thm ctxt';
fun label_thm thm =
Thm.cterm_of ctxt'' (Free (nm, propT))
|> Drule.mk_term
|> not (null abs_nms) ? Conjunction.intr thm
val _ =
ListPair.app
(fn ((SOME ((v, _)), _), SOME (t, _)) => Parse_Tools.the_parse_fun v t
| ((NONE, _), NONE) => ()
| _ => error "Mismatch between real and parsed bound variables")
(ts, binds');
val real_fixes' = map (Morphism.term morphism) real_fixes; val _ =
ListPair.app (fn (((v, _) , _), t) => Parse_Tools.the_parse_fun v t)
(fixes, real_fixes');
val match_args = map (fn (_, (_, match_args)) => match_args) ts; val binds'' = (binds' ~~ match_args) ~~ pats';
val src' = map (Token.transform morphism) src; val _ = Token.assign (SOME (Token.Source src')) body; in
(binds'', real_fixes', text) end));
fun dest_internal_term t =
(casetry Logic.dest_conjunction t of
SOME (params, head) =>
(params |> Logic.dest_conjunctions |> map Logic.dest_term,
head |> Logic.dest_term)
| NONE => ([], t |> Logic.dest_term));
fun dest_internal_fact thm = dest_internal_term (Thm.prop_of thm);
fun inst_thm ctxt env ts params thm = let val ts' = map (Envir.norm_term env) ts; val insts = map (#1 o dest_Var) ts' ~~ map (Thm.cterm_of ctxt) params; in infer_instantiate ctxt insts thm end;
fun do_inst fact_insts' env text ctxt = let val fact_insts =
map_filter
(fn ((((SOME ((_, head), att), _), _), _), thms) => SOME (head, (thms, att))
| _ => NONE) fact_insts';
fun try_dest_term thm = \<^try>\<open>#2 (dest_internal_fact thm)\<close>;
fun expand_fact fact_insts thm =
the_default [thm]
(case try_dest_term thm of
SOME t_ident => AList.lookup (op aconv) fact_insts t_ident
| NONE => NONE);
val all_params_bound = forall (fn SOME (_, Free (x, _)) => is_newly_fixed x | _ => false) params'; val all_params_distinct = not (has_duplicates (eq_option (eq_pair (op =) (op aconv))) params');
val pat_fixes = inter (eq_fst (op =)) fixes_vars pat_vars; val all_pat_fixes_bound = forall (fn (xi, _) => is_some (Vartab.lookup tenv' xi)) pat_fixes; in if all_params_bound andalso all_pat_fixes_bound andalso all_params_distinct then SOME env' else NONE end;
(* Slightly hacky way of uniquely identifying focus premises *) val prem_idN = "premise_id";
fun prem_id_eq ((id, _ : thm), (id', _ : thm)) = id = id';
val prem_rules : (int * thm) Item_Net.T =
Item_Net.init prem_id_eq (single o Thm.full_prop_of o snd);
fun raw_thm_to_id thm =
(case Properties.get (Thm.get_tags thm) prem_idN of NONE => NONE | SOME id => Int.fromString id)
|> the_default ~1;
structure Focus_Data = Proof_Data
( type T =
(int * (int * thm) Item_Net.T) * (*prems*)
Envir.tenv * (*schematics*)
term list(*params*) fun init _ : T = ((0, prem_rules), Vartab.empty, [])
);
(* focus prems *)
val focus_prems = #1 o Focus_Data.get;
fun transfer_focus_prems from_ctxt =
Focus_Data.map (@{apply 3(1)} (K (focus_prems from_ctxt)))
fun remove_focus_prem' (ident, thm) =
(Focus_Data.map o @{apply 3(1)} o apsnd)
(Item_Net.remove (ident, thm));
fun remove_focus_prem thm = remove_focus_prem' (raw_thm_to_id thm, thm);
(*TODO: Preliminary analysis to see if we're trying to clear in a non-focus match?*) val _ =
Theory.setup
(Attrib.setup \<^binding>\<open>thin\<close>
(Scan.succeed
(Thm.declaration_attribute (fn th => Context.mapping I (remove_focus_prem th)))) "clear premise inside match method");
(* focus schematics *)
val focus_schematics = #2 o Focus_Data.get;
fun add_focus_schematics schematics =
(Focus_Data.map o @{apply 3(2)})
(fold (fn ((xi, T), ct) => Vartab.update_new (xi, (T, Thm.term_of ct))) schematics);
(* focus params *)
val focus_params = #3 o Focus_Data.get;
fun add_focus_params params =
(Focus_Data.map o @{apply 3(3)})
(append (map (fn (_, ct) => Thm.term_of ct) params));
(* Add focus elements as proof data *) fun augment_focus (focus: Subgoal.focus) : (int list * Subgoal.focus) = let val {context, params, prems, asms, concl, schematics} = focus;
(* Fix schematics in the goal *) fun focus_concl ctxt i bindings goal = let val ({context = ctxt', concl, params, prems, asms, schematics}, goal') =
Subgoal.focus_params ctxt i bindings goal;
fun deduplicate eq prev seq =
Seq.make (fn () =>
(case Seq.pull seq of
SOME (x, seq') => if member eq prev x then Seq.pull (deduplicate eq prev seq') else SOME (x, deduplicate eq (x :: prev) seq')
| NONE => NONE));
fun consistent_env env = let val tenv = Envir.term_env env; val tyenv = Envir.type_env env; in
Vartab.forall (fn (_, (T, t)) => Envir.norm_type tyenv T = fastype_of t) tenv end;
fun merge_env (env1, env2) = let val tenv =
Vartab.merge (eq_snd (term_eq_wrt (env1, env2))) (Envir.term_env env1, Envir.term_env env2); val tyenv =
Vartab.merge (eq_snd (type_eq_wrt (env1, env2)) andf eq_fst (op =))
(Envir.type_env env1, Envir.type_env env2); val maxidx = Int.max (Envir.maxidx_of env1, Envir.maxidx_of env2); in Envir.Envir {maxidx = maxidx, tenv = tenv, tyenv = tyenv} end;
fun import_with_tags thms ctxt = let val ((_, thms'), ctxt') = Variable.import false thms ctxt; val thms'' = map2 (fn thm => Thm.map_tags (K (Thm.get_tags thm))) thms thms'; in (thms'', ctxt') end;
fun try_merge (env, env') = SOME (merge_env (env, env')) handle Vartab.DUP _ => NONE
fun Seq_retrieve seq f = let fun retrieve' (list, seq) f =
(case Seq.pull seq of
SOME (x, seq') => if f x then (SOME x, (list, seq')) else retrieve' (list @ [x], seq') f
| NONE => (NONE, (list, seq)));
val (result, (list, seq)) = retrieve' ([], seq) f; in (result, Seq.append (Seq.of_list list) seq) end;
fun match_facts ctxt fixes prop_pats get = let fun is_multi (((_, x : match_args), _), _) = #multi x; fun get_cut (((_, x : match_args), _), _) = #cut x; fun do_cut n = if n = ~1 then I else Seq.take n;
val raw_thmss = map (get o snd) prop_pats; val (thmss, ctxt') = fold_burrow import_with_tags raw_thmss ctxt;
val newly_fixed = Variable.is_newly_fixed ctxt' ctxt;
val morphism = Variable.export_morphism ctxt' ctxt;
fun match_thm (((x, params), pat), thm) = let val pat_vars = Term.add_vars pat [];
fun proc_multi_match (pat, thmenvs) (pats, env) =
do_cut (get_cut pat)
(if is_multi pat then let fun maximal_set tail seq envthms =
Seq.make (fn () =>
(case Seq.pull seq of
SOME ((thm, env'), seq') => let val (result, envthms') =
Seq_retrieve envthms (fn (env, _) => eq_env (env, env')); in
(case result of
SOME (_, thms) => SOME ((env', thm :: thms), maximal_set tail seq' envthms')
| NONE => Seq.pull (maximal_set (tail @ [(env', [thm])]) seq' envthms')) end
| NONE => Seq.pull (Seq.append envthms (Seq.of_list tail))));
val maximal_sets = fold (maximal_set []) thmenvs Seq.empty; in
maximal_sets
|> Seq.map swap
|> Seq.filter (fn (thms, _) => not (null thms))
|> Seq.map_filter (fn (thms, env') =>
(case try_merge (env, env') of
SOME env'' => SOME ((pat, thms) :: pats, env'')
| NONE => NONE)) end else let fun just_one (thm, env') =
(case try_merge (env, env') of
SOME env'' => SOME ((pat, [thm]) :: pats, env'')
| NONE => NONE); in fold (fn seq => Seq.append (Seq.map_filter just_one seq)) thmenvs Seq.empty end);
val all_matches =
Seq.EVERY (map proc_multi_match all_matches) ([], Envir.init); in
all_matches
|> Seq.map (apsnd (morphism_env morphism)) end;
fun real_match using outer_ctxt fixes m text pats st = let val goal_ctxt =
fold Variable.declare_term fixes outer_ctxt (*FIXME Is this a good idea? We really only care about the maxidx*)
|> fold (fn (_, t) => Variable.declare_term t) pats;
fun make_fact_matches ctxt get = let val (pats', ctxt') = fold_map prep_fact_pat pats ctxt; in
match_facts ctxt' fixes pats' get
|> Seq.map (fn (fact_insts, env) => do_inst fact_insts env text ctxt') end;
fun make_term_matches ctxt get = let val pats' = map
(fn ((SOME _, _), _) => error "Cannot name term match"
| ((_, x), t) => (((NONE, x), []), Logic.mk_term t)) pats;
val thm_of = Drule.mk_term o Thm.cterm_of ctxt; fun get' t = get (Logic.dest_term t) |> map thm_of; in
match_facts ctxt fixes pats' get'
|> Seq.map (fn (fact_insts, env) => do_inst fact_insts env text ctxt) end; in
(case m of
Match_Fact net =>
make_fact_matches goal_ctxt (Item_Net.retrieve net)
|> Seq.map (fn (text, ctxt') =>
Method.evaluate_runtime text ctxt' using (ctxt', st)
|> Seq.filter_results |> Seq.map (fn (_, thm) => (outer_ctxt, thm)))
| Match_Term net =>
make_term_matches goal_ctxt (Item_Net.retrieve net)
|> Seq.map (fn (text, ctxt') =>
Method.evaluate_runtime text ctxt' using (ctxt', st)
|> Seq.filter_results |> Seq.map (fn (_, thm) => (outer_ctxt, thm)))
| match_kind => if Thm.no_prems st then Seq.empty else let fun focus_cases f g =
(case match_kind of
Match_Prems b => f b
| Match_Concl => g
| _ => raise Fail "Match kind fell through");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.