(* ========================================================================= *)
(*                              Isabelle Light                               *)
(*   Isabelle/Procedural style additions and other user-friendly shortcuts.  *)
(*                                                                           *)
(*                   Petros Papapanagiotou, Jacques Fleuriot                 *)
(*              Center of Intelligent Systems and their Applications         *)
(*                          University of Edinburgh                          *)
(*                                 2009-2012                                 *)
(* ========================================================================= *)
(* FILE         : meta_rules.ml                                              *)
(* DESCRIPTION  : Meta rules is a formalisation used to accommodate          *)
(*                Isabelle's inference rules in HOL Light.The technical      *)
(*                details are described in the comments that follow.         *)
(*                Isabelle rule application tactics (rule, erule, etc.) have *)
(*                been defined to work with meta rules.                      *)
(*                We have not been able to accommodate first order rules     *)
(*                allI and exE. We also make use of metavariables which are  *)
(*                restricted by the limitations of term_unify                *)
(*                (ie. no HO unification and no type instantiation).         *)
(* LAST MODIFIED: October 2012                                               *)
(* ========================================================================= *)
(* ------------------------------------------------------------------------- *)
(* ----------------------- META-LEVEL IMPLICATION -------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Emulation of meta-level implication at the object level.                  *)
(* This is purely for syntax and parsing purposes. It solves a number of     *)
(* problems when parsing theorems as meta-rules (see below).                 *)
(* It is applied at the logic level only for transparency.                   *)
(* ------------------------------------------------------------------------- *)
(* Thanks to Phil Scott for the suggestion.                                  *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Syntax definition.                                                        *)
(* ------------------------------------------------------------------------- *)
parse_as_infix ("===>",(4,"right"));;
let is_mimp = is_binary "===>";;
let dest_mimp = dest_binary "===>";;
(* ------------------------------------------------------------------------- *)
(* Logic definition: Equivalent to object-level implication.                 *)
(* ------------------------------------------------------------------------- *)
let MIMP_DEF = new_basic_definition
  `(===>) = \p q. p ==> q`;;
(* ------------------------------------------------------------------------- *)
(* CONV, RULE and TACTIC to get rid of meta-level implication in proofs.     *)
(* ------------------------------------------------------------------------- *)
let MIMP_TO_IMP_CONV = BETA_RULE o (PURE_REWRITE_CONV [MIMP_DEF]);;
let MIMP_TO_IMP_RULE = BETA_RULE o (PURE_REWRITE_RULE [MIMP_DEF]);;
let MIMP_TAC = (PURE_REWRITE_TAC [MIMP_DEF]) THEN BETA_TAC;;
(* ------------------------------------------------------------------------- *)
(* Equivalent of TAUT after getting rid of meta-level implication.           *)
(* Helps prove simple propositional meta-rules easily.                       *)
(* ------------------------------------------------------------------------- *)
let MTAUT tm =
    let th = MIMP_TO_IMP_CONV tm in
    EQ_MP (SYM th) ((TAUT o snd o dest_iff o concl) th);;
(* ------------------------------------------------------------------------- *)
(* RULE to replace implication by meta-level implication to easily create    *) 
(* meta-theorems from normal theorems.                                       *)
(* ------------------------------------------------------------------------- *)
let MIMP_THM = MTAUT `(p==>q) <=> (p===>q)`;;
let MIMP_RULE = PURE_REWRITE_RULE[MIMP_THM];;
(* ------------------------------------------------------------------------- *)
(* UNDISCH for meta-level implication.                                       *)
(* Also gets rid of meta-level implication in the undischarged term.         *)
(* ------------------------------------------------------------------------- *)
let MUNDISCH th = 
    let mth = BETA_RULE (AP_THM (AP_THM MIMP_DEF `p:bool`) `q:bool`) in 
    let th =  PURE_ONCE_REWRITE_RULE [mth] th in
    try let undisch_tm = (rand o rator o concl) th in
    PROVE_HYP ((UNDISCH o snd o EQ_IMP_RULE o MIMP_TO_IMP_CONV) undisch_tm) (UNDISCH th)
    with Failure _ -> failwith "MUNDISCH";;
(* ------------------------------------------------------------------------- *)
(* -------------------------- HELPFUL FUNCTIONS ---------------------------- *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* REV_PART_MATCH_I: term list -> (term -> term) -> thm -> term              *)
(*                                                         -> instantiation  *)
(* Does a reverse PART_MATCH and returns the resulting instantiation.        *)
(* Avoids instantiating any of the given variables/constants.                *)
(* Does not apply SPEC_ALL like PART_MATCH does.                             *)
(* ------------------------------------------------------------------------- *)
(* The original PART_MATCH matches a term to part of a theorem so that we can*)
(* instantiate that part with the term.                                      *)
(* The reverse used here, matches the part of the theorem with the term so   *)
(* that the term can be instantiated with the part of the theorem.           *)
(* We use this in cases such as erule where we want (part of) an assumption  *)
(* to match a premise of the rule. We need the instantiation of the rule when*)
(* matched to the assumption (thm) and not the other way around.             *)
(* ------------------------------------------------------------------------- *)
let REV_PART_MATCH_I =
  let rec match_bvs t1 t2 acc =
    try let v1,b1 = dest_abs t1
        and v2,b2 = dest_abs t2 in
        let n1 = fst(dest_var v1) and n2 = fst(dest_var v2) in
        let newacc = if n1 = n2 then acc else insert (n1,n2) acc in
        match_bvs b1 b2 newacc
    with Failure _ -> try
        let l1,r1 = dest_comb t1
        and l2,r2 = dest_comb t2 in
        match_bvs l1 l2 (match_bvs r1 r2 acc)
    with Failure _ -> acc in
  fun avoids partfn th ->
    let bod = concl th in
    let pbod = partfn bod in
    let lconsts = union avoids (intersect (frees (concl th)) (freesl(hyp th))) in
    fun tm ->
      let bvms = match_bvs pbod tm [] in
      let atm = deep_alpha bvms tm in
      term_match lconsts atm (partfn bod) ;; (* whereas in PART_MATCH we do it the other way around *)
(* ------------------------------------------------------------------------- *)
(* term_to_asm_match : term list -> term -> (string * thm) list ->           *)
(*                               (string * thm) list * (thm * instantiation) *)
(* ------------------------------------------------------------------------- *)
(* term_to_asm_match tries to match key to one of the assumptions using      *)
(* REV_PART_MATCH_I. Returns the new assumption list (with the matching      *)
(* assumption removed), the matching assumption and the resulting            *)
(* instantiation used.                                                       *)
(* ------------------------------------------------------------------------- *)
(* It is doubtful that this has practical use outside the Xrule_tac's.       *)
(* It is used in erule, drule and frule to match the major premise to one of *)
(* the assumptions.                                                          *)
(* ------------------------------------------------------------------------- *)
let rec (term_to_asm_match: term list -> term -> (string * thm) list -> (string * thm) list * (thm * instantiation)) =
  fun avoids key asms ->
    if (asms = []) then failwith ("No assumptions match `" ^ (string_of_term key) ^ "`!")
    else try 
      let asm = (snd o hd) asms in
      let i = REV_PART_MATCH_I avoids I asm key in
      (tl asms),(asm,i)
    with Failure _ -> let res,inst = term_to_asm_match avoids key (tl asms) in ((hd asms)::res),inst;;
(* ------------------------------------------------------------------------- *)
(* term_to_asm_n_match : term list -> term -> (string * thm) list -> int ->  *)
(*                               (string * thm) list * (thm * instantiation) *)
(* ------------------------------------------------------------------------- *)
(* Same as term_to_asm_match but only tries to match nth assumption.         *)
(* ------------------------------------------------------------------------- *)
(* It is doubtful that this has practical use outside the Xrulen_tac's.      *)
(* It is used in erulen, drulen and frulen to match the major premise to one *)
(* of the assumptions.                                                       *)
(* ------------------------------------------------------------------------- *)
let rec (term_to_asm_n_match: term list -> term -> (string * thm) list -> int -> (string * thm) list * (thm * instantiation)) =
  fun avoids key asms n ->
    if (asms = []) then failwith "No such assumption found!"
    else try match n with
	0 ->  
	  let asm = (snd o hd) asms in
	  let i = REV_PART_MATCH_I avoids I asm key in
	    (tl asms),(asm,i)
      | _ -> let re_asms,m = term_to_asm_n_match avoids key (tl asms) (n-1) in
	  (hd asms)::re_asms,m
    with Failure _ -> failwith ("Assumption `" ^ ((string_of_term o concl o snd o hd) asms) ^ "` doesn't match `" ^ (string_of_term key) ^ "`!");;
(* gmm is not to be used until qed is updated *)
(* We need a MDISCH for that...               *)
let gmm t =
  let fvs = sort (<) (map (fst o dest_var) (frees t)) in
  (if fvs <> [] then
     let errmsg = end_itlist (fun s t -> s^", "^t) fvs in
     warn true ("Free variables in goal: "^errmsg)
   else ());
  let rec split_mimp = fun tm ->
    if (is_mimp tm) 
    then 
      let (a,b) = dest_mimp tm in
      let (asms, concl) = split_mimp b in
      (a::asms,concl)
    else ([],tm) in
   set_goal (split_mimp t);;
(* ------------------------------------------------------------------------- *)
(* gm : term -> goalstack                                                    *)
(* This is used to set a term containing meta-level implication as a goal.   *)
(* ------------------------------------------------------------------------- *)
(* (+) Uses g to set the goal then MIMP_TAC to get rid of meta-implication.  *)
(* (+) Note that if the goal has normal implication it gets discharged as    *)
(* well. This will be fixed when gmm is fixed.                               *)
(* ------------------------------------------------------------------------- *)
let gm t = g t ; e (MIMP_TAC THEN REPEAT DISCH_TAC);;
(* ------------------------------------------------------------------------- *)
(* Isabelle's natural deduction rules as thms with meta-level implication.   *)
(* ------------------------------------------------------------------------- *)
let conjI = MTAUT `p===>q===>p/\q`;;
let conjunct1 = MTAUT `p/\q===>p`;;
let conjunct2 = MTAUT `p/\q===>q`;;
let conjE = MTAUT `p/\q===>(p===>q===>r)===>r`;; 
let disjI1 = MTAUT `p===>p\/q`;;
let disjI2 = MTAUT `q===>p\/q`;;
let disjE = MTAUT `p\/q===>(p===>r)===>(q===>r)===>r`;;
let impI = MTAUT `(p===>q)===>(p==>q)`;;
let impE = MTAUT `(p==>q)===>p===>(q===>r)===>r`;;
let mp = MTAUT `(p==>q)===>(p===>q)`;;
let iffI = MTAUT `(a===>b)===>(b===>a)===>(a<=>b)`;;
let iffE = MTAUT `(a<=>b)===>((a==>b) ===> (b==>a) ===> r) ===> r`;;
let allE = prove( `(!x:A. P x) ===> (P (a:A) ===> (r:bool)) ===> r` ,
                  MIMP_TAC THEN MESON_TAC[]);;
 
let exI = prove (`P (a:A)===> ?x:A. P x`,
 
                          MIMP_TAC THEN
			  DISCH_TAC THEN 
			    (EXISTS_TAC `a:A`) THEN 
			    (FIRST_ASSUM ACCEPT_TAC));;